EasyNetQ使用(八)【对延迟消息插件的支持,自动订阅者】
RabbitMQ延迟消息插件仍然在实验阶段。你使用这个功能要自担风险。
RabbitMQ延迟消息插件为RabbitMQ
增加了新的交换机类型,允许延时消息投递。
EasyNetQ
为交换机通过定义一种新的日程类型:DelayedExchangeScheduler
来支持这种能力。
这样允许你使用之前同样的Future Publish
接口,但是取消Future Message
会抛出异常。因为延迟消息插件不支持消息取消,不管你在调用FuturePublish
是否指定了cancellationKey
,或当你调用CancelFuturePublish
时,调度器将会抛出NotImplementedException
异常。
下面例子展示的是:你如何发布一个消息,这个消息会在未来3个月后收到这个消息。
bus = RabbitHutch.CreateBus("host=localhost",
x=> x.Register<IScheduler,DelayedExchangeScheduler>());
var followUpCallMessage = new FollowUpCallMessage( .. );
bus.FuturePublish(DateTime.UtcNow.AddMonths(3), followUpCallMessage);
第一行代码告示EasyNetQ
使用新的支持延迟消息交换机作为调度器。下面,消息被创建,然后在发布时,指定了投递时间是3个月后。注意:FuturePublish
使用了UTC
时间。
插件安装
可以在Community Plugins page找到延迟消息插件。下载与你安装的RabbitMQ
对应 .ez
文件,拷贝这个文件到RabbitMQ plugin
文件夹下,然后通过运行下面的命令启动之。
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
这个插件要求RabbitMQ最低版本为3.4或更新版本。
它是怎么运行的
当你调用bus.FuturePublish(..)
,EasyNetQ跟正常的交换机和binds
一起,自动创建了一个新的 x-delayed-message
交换机,这个消息被发送到这个延迟交换机上,并存储这个消息,直到到期时去投递这个消息。就在投递这个时刻,这个消息会被路由到普通交换机,然后从普通交换机到绑定的队列中。
当你调用Publish(..)
方法,消息会发布到正常交换机, 防止降低使用与x-delayed-message
有关系的交换机的性能。
延迟交换机持久化消息使用了Mnesia
.这样避免在服务器停机时丢失消息。在服务器恢复之后,所有符合条件的消息将会按计划日程投递。
取消Future消息
如前所述,延迟消息插件不支持消息取消,因此这个功能是不支持的。任何调用FuturePublish
时指定cancellationKey
,或者调用CancelFuturePublish
,都会抛出NotImplementedException
异常。
如果你需要这个功能,请上一篇所讲的使用Scheduler
服务,具体参考EasyNetQ
之用Future Publish
发布预定中事件
EasyNetQ v0.7.1.30
版本有了一个简单AutoSubscriber
。你能够使用它很容易的去扫描指定程序集中实现了IConsume
或 IConsumeAsync
接口的类,然后这个自动订阅者让这些消费者订阅到你的事件总线中。IConsume
的实现将使用事件总线的Subscribe
方法,同时IConsumeAsync
的实现使用事件总线的SubscribeAsync
方法,详情参看EasyNetQ
之订阅。你当然能够让你的消费者处理多个消息。让我们看一些示例。
注意:从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
实例和subscriberId
前缀给AutoSubscriber
的构造函数。这个subscriberId
前缀会作为所有自动生成的subscriberIds
的前缀。但是不能自定义subscriberId
(参看下文)。
注册在相同程序集中的所有其他消费者,我们仅仅需要去传包含你的消费者所在的程序集给这个方法:AutoSubscriber.Subscribe(assembly)
。注意!,这些事情你应该只做一次,最好在应用启动的时候。
var subscriber = new AutoSubscriber(bus,
"my_applications_subscriptionId_prefix");
subscriber.Subscribe(Assembly.GetExecutingAssembly());
通过主题订阅
默认的AutoSubscriber
将不带主题的绑定。下面的例子中MessageA
被注册到两个主题。
注意! 如果你运行下面代码,没有加上ForTopic
属性标签,它将有一个”#”路由Key,这个Key将会选择任何订阅这个消息类型的订阅者。假定在默认端口上且admin插件已被安装,简单访问http://localhost:15672/#/queues,如果需要请解除绑定的路由。
public class MyConsumer : IConsume<MessageA>,
IConsume<MessageB>,
IConsumeAsync<MessageC>
{
[ForTopic("Topic.Foo")]
[ForTopic("Topic.Bar")]
public void Consume(MessageA message) {...}
public void Consume(MessageB message) {...}
public Task Consume(MessageC message) {...}
}
//通过主题发布
var bus = RabbitHutch.CreateBus("host=localhost");
//被选中
var msg1 = new MessageA("topic.foo");
bus.Publish(msg1, "Topic.Foo");
//被选中
var msg2 = new MessageA("topic.bar");
bus.Publish(msg1, "Topic.Bar");
//不会被选中
var msg3 = new MessageA("no pick up);
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
替换就可以实现。
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);
现在每一次消息到来是,一个新的消费者实例,将会从我们自定义的容器中拿到。
EasyNetQ使用(八)【对延迟消息插件的支持,自动订阅者】的更多相关文章
- 15-EasyNetQ之对延迟消息插件的支持
RabbitMQ延迟消息插件仍然在实验阶段.你使用这个功能要自担风险. RabbitMQ延迟消息插件为RabbitMQ增加了新的交换机类型,允许延时消息投递. EasyNetQ为交换机通过定义一种新的 ...
- RabbitMQ延迟消息学习
准备做一个禁言自动解除的功能,立马想到了订单的超时自动解除,刚好最近在看RabbitMQ的实现,于是想用它实现,查询了相关文档发现确实可以实现,动手编写了这篇短文. 准备工作 1.Erlang安装请参 ...
- Spring Cloud Stream 使用延迟消息实现定时任务(RabbitMQ)
应用场景 通常在应用开发中我们会碰到定时任务的需求,比如未付款订单,超过一定时间后,系统自动取消订单并释放占有物品. 许多同学的第一反应就是通过spring的schedule定时任务轮询数据库来实现, ...
- SpringCloud 2020.0.4 系列之 Stream 延迟消息 的实现
1. 概述 老话说的好:对待工作要有责任心,不仅要完成自己的部分,还要定期了解整体的进展. 言归正传,我们在开发产品时,常常会遇到一段时间后检查状态的场景,例如:用户下单场景,如果订单生成30分钟后, ...
- RocketMQ系列(五)广播与延迟消息
今天要给大家介绍RocketMQ中的两个功能,一个是"广播",这个功能是比较基础的,几乎所有的mq产品都是支持这个功能的:另外一个是"延迟消费",这个应该算是R ...
- RocketMQ延迟消息的代码实战及原理分析
RocketMQ简介 RocketMQ是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的.高可靠.万亿级容量.灵活可伸缩的消息发布与订阅服务. 它前身是MetaQ,是阿里基于Kafka ...
- RabbitMQ延迟消息:死信队列 | 延迟插件 | 二合一用法+踩坑手记+最佳使用心得
前言 前段时间写过一篇: # RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得 很多人加了我好友,说很喜欢这篇文章,也问了我一些问题. 因为最近工作比较忙, ...
- Spring Boot RabbitMQ 延迟消息实现完整版
概述 曾经去网易面试的时候,面试官问了我一个问题,说 下完订单后,如果用户未支付,需要取消订单,可以怎么做 我当时的回答是,用定时任务扫描DB表即可.面试官不是很满意,提出: 用定时任务无法做到准实时 ...
- laravel的延迟消息队列
laravel的延迟消息队列 这篇来自于看到朋友转的58沈剑的一篇文章:1分钟实现"延迟消息"功能(http://mp.weixin.qq.com/s?__biz=MjM5ODYx ...
随机推荐
- c++两种字符串赋值方式 并介绍 C语言下遍历目录文件的方式
c++字符串声明:一种是声明字符数组并赋值,另一种是直接声明string类 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #incl ...
- mongodb 高级聚合查询
mongodb高级聚合查询 在工作中会经常遇到一些mongodb的聚合操作,特此总结下.mongo存储的可以是复杂类型,比如数组.对象等mysql不善于处理的文档型结构,并且聚合的操作也比mysq ...
- 【java8新特性】日期和时间
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本. Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript ...
- Allure自动化测试报告之修改allure测试报告logo
1.安装allure 2.进入 /usr/local/Cellar/allure/2.10.0/libexec/config 3.在allure.yml添加 - custom-logo-plugin ...
- linux 网络带宽和延时测试
Linux下使用qperf命令来测试网络带宽和网络延迟 参考文章:https://access.redhat.com/solutions/2122681 若是没有安装qperf命令,请使用yum 安装 ...
- combobox放入数据
页面 <th width="15%">国际分类号</th><td width="30%"> <select cla ...
- js 根据滚动条加载数据
很久没记笔记了,最近搞起web开发了 <html> <head> <script src="http://code.jquery.com/jquery-1.7. ...
- 从xml中返回的对象,和new 返回的对象时不同的。
public BigDecimal getTax() { return tax == null ? BigDecimal.ZERO : tax; } 这是自定义的一个类 对null 做出了处理. 但是 ...
- Python 学习随笔 - 2 - list 、tuple 、dict、set 特殊数据类型 及 实际应用
1.list list是一种有序的集合,可以随时添加和删除其中的元素; 和C语言不同的地方是list里的元素甚至可以是不同类型的,甚至是另个list 例如:['A', 'B', 'C'] ['A ...
- legend3---lamp.sh常用操作
legend3---lamp.sh常用操作 一.总结 一句话总结: 1.github上下载代码 2.修改项目数据库配置,修改文件权限等操作:chown -R apache:apache /data/w ...