18、Auto Subscriber 自动订阅
EasyNetQ v0.7.1.30版本出了一个简单的AutoSubscriber。你能够用它很容易地扫描指定程序集中实现了IConsume 或 IConsumeAsync接口的类,然后让这些消费者订阅到你的bus上。IConsume的实现将使用bus的Subscribe方法,同时IConsumeAsync的实现使用bus的SubscribeAsync方法,详情参看《9、Subscribe订阅》。当然你可以让你的消费者处理多个消息。让我们看一些示例。
注意:从0.13.0版本开始,所有的AutoSubscriber类都在EasyNetQ.AutoSubscribe命名空间中。因此请添加下面的using语句。
using EasyNetQ.AutoSubscribe;
让我们定义一个简单消费者,处理三个消息:MessageA,MessageB和MessageC。
public class MyConsumer : IConsume<MessageA>, IConsume<MessageB>, IConsumeAsync<MessageC> { public void Consume(MessageA message) {...} public void Consume(MessageB message) {...} public Task Consume(MessageC message) {...} }
首先创建一个新的AutoSubscriber实例,传入你的IBus实例和subscriptionId_prefix前缀给AutoSubscriber的构造函数。这个subscriptionId_prefix前缀会作为所有自动生成的subscriber_Id的前缀。而不是自定义的subscriberId(参看下文)。
注册在相同程序集中的所有其他消费者,我们仅仅需要去传入包含你的消费者所在的程序集给这个方法:AutoSubscriber.Subscribe(assembly)。注意!,这些事情你应该只做一次,最好在应用启动的时候。
var subscriber = new AutoSubscriber(bus, "my_applications_subscriptionId_prefix");
subscriber.Subscribe(Assembly.GetExecutingAssembly());
通过主题订阅
默认地AutoSubscriber将绑定不带主题的消息。下面的例子中MessageA注册了两个主题。
注意! 如果你运行下面代码,没有加上ForTopic特性标签,它将有一个"#"路由键,这个路由键将会选择这个消息类型的任何订阅。假设默认的端口和管理插件已安装,简单访问http://localhost:15672/#/queues,如果需要请解除绑定的路由。
1 public class MyConsumer : IConsume<MessageA>, 2 IConsume<MessageB>, 3 IConsumeAsync<MessageC> 4 { 5 [ForTopic("Topic.Foo")] 6 [ForTopic("Topic.Bar")] 7 public void Consume(MessageA message) {...} 8 9 public void Consume(MessageB message) {...} 10 11 public Task Consume(MessageC message) {...} 12 } 13 14 //通过主题发布 15 var bus = RabbitHutch.CreateBus("host=localhost"); 16 17 //被选中 18 var msg1 = new MessageA("topic.foo"); 19 bus.Publish(msg1, "Topic.Foo"); 20 21 //被选中 22 var msg2 = new MessageA("topic.bar"); 23 bus.Publish(msg1, "Topic.Bar"); 24 25 //不会被选中 26 var msg3 = new MessageA("no pick up); 27 bus.Publish(msg3);
指定一个特定的SubscriptionId
AutoSubscriber 默认情况下为每一组Message/Consumer生成一个唯一的SubscriptionId。这意味着当相同的消费者启动多个实例后,这些实例将会从相同队列中循环消费消息。
如果你希望subscriptionId是固定的,你能通过带有AutoSubscriberConsumerAttribute属性标签的Consume方法实现。为什么你会让SubscriptionId是固定的,可以阅读这里。
假如,消费MessageB消息的Consume方法需要固定的SubscriptionId。仅仅需要使用AutoSubscriberConsumerAttribute属性标签,为其SubscriptionId设置一个值。
[AutoSubscriberConsumer(SubscriptionId = "MyExplicitId")]
public void Consume(MessageB message) { }
控制SubscriptionId的生成
你当然也可以控制实际的SubscriptionId的生成,只要用AutoSubscriber.GenerateSubscriptionId : Func<consumerinfo, string="">替换就可以实现。
var subscriber = new AutoSubscriber(bus)
{
CreateConsumer = t => objectResolver.Resolve(t),
GenerateSubscriptionId = c => AppDomain.CurrentDomain.FriendlyName
+ c.ConcreteType.Name
};
subscriber.Subscribe(Assembly.GetExecutingAssembly());
注意! 这只是实现的一个样例。请确保你已经读过并理解了SubscriptionId值的重要价值。
控制消费者配置设置
当使用autosubsriber去订阅队列消息,你能够设置ISubscriptionConfiguration值,例如AutoDelete,Priority 等。
var subscriber = new AutoSubscriber(bus)
{
ConfigureSubscriptionConfiguration = c => c.WithAutoDelete()
.WithPriority(10)
};
subscriber.Subscribe(Assembly.GetExecutingAssembly());
或者,你可以用在consume方法上用属性标签的方式。
public class MyConsumer : IConsume<MessageA>
{
[SubscriptionConfiguration(CancelOnHaFailover = true, PrefetchCount = 10)]
public void Consume(MessageA message) {...}
}
让AutoSubscriber使用容器
AutoSubscriber 有一个属性:MessageDispatcher,这个属性允许插入你自己消息调度代码。这样允许你从IoC容器中或者其他第三方任务调度中,解析你自己的消费者。
让我们写一个自定义的IAutoSubscriberMessageDispatcher实现,从 Windsor IoC 容器中去解析消费者。
public class WindsorMessageDispatcher : IAutoSubscriberMessageDispatcher
{
private readonly IWindsorContainer container;
public WindsorMessageDispatcher(IWindsorContainer container)
{
this.container = container;
}
public void Dispatch<TMessage, TConsumer>(TMessage message) where TMessage : class where TConsumer : IConsume<TMessage>
{
var consumer = container.Resolve<TConsumer>();
try
{
consumer.Consume(message);
}
finally
{
container.Release(consumer);
}
}
public Task DispatchAsync<TMessage, TConsumer>(TMessage message) where TMessage: class where TConsumer: IConsumeAsync<TMessage>
{
var consumer = _container.Resolve<TConsumer>();
return consumer.Consume(message).ContinueWith(t=>_container.Release(consumer));
}
}
现在,我们需要注册消费者到我们的IoC容器中。
var container = new WindsorContainer();
container.Register(
Component.For<MyConsumer>().ImplementedBy<MyConsumer>()
);
下一步,让AutoSubscriber 用我们自定义的IMessageDispatcher:
var bus = RabbitHutch.CreateBus("host=localhost");
var autoSubscriber = new AutoSubscriber(bus, "My_subscription_id_prefix")
{
MessageDispatcher = new WindsorMessageDispatcher(container)
};
autoSubscriber.Subscribe(GetType().Assembly);
autoSubscriber.SubscribeAsync(GetType().Assembly);
现在每一次消息到来是,一个新的消费者实例,将会从我们自定义的容器中拿到。
英文地址:https://github.com/EasyNetQ/EasyNetQ/wiki/Auto-Subscriber