前言
前段时间写过一篇:
[# RabbitMQ:音讯失落 | 音讯反复 | 音讯积压的起因 + 解决方案 + 网上学不到的应用心得]
很多人加了我好友,说很喜爱这篇文章,也问了我一些问题。
因为最近工作比较忙,隔了一段时间没写,忙完后专门花工夫把 RabbitMQ 剩下的一个重要技术点通过案例的形式整理出来,就是提早音讯的用法。
提早音讯含意不解释了,就是字面意思。
用法一共两种形式,死信队列和提早插件,两种各有利弊,我会一一陈说并给出最佳用法。
<br/>
死信队列形式
死信队列不要了解成很玄乎的货色,它就是一般队列绑定了死信交换机,而且配置参数还是固定的,无需动脑,作用的话你设想成回收站就好了,被回绝或超时的音讯就往这里边丢,而后还能持续被生产,就这么简略。
1、原理图解
<br/>
2、引入 MQ
3、申明交换机和队列
申明一般交换机、队列、路由,这里咱们申明两个准备提早的队列,名称别离蕴含 5s 和 15min,用来辨别提早音讯是否达到预期成果。
咱们接下来所有交换机和队列都是以 Direct 模式来创立的,也就是点对点形式,具体起因前面会讲。
另外,留神这里正文的提早交换机、队列,都是为了特地阐明,其实还是一般队列,参考下面的原理图解。
创立交换机、队列、绑定关系。
聪慧的小伙伴应该能发现,下面这段代码只有交换机和绑定队列的关系,却没有创立队列。
没错,接下来就是重点局部,创立队列时,要绑定死信交换机,这样就变成了一个死信队列。
能够看到,5s 和 15min 的队列绑定的都是同一个死信交换机,只是路由规定、音讯过期工夫 TTL 不同。
这样,在我的项目启动后,RabbitMQ 就会创立出两个具备不同过期工夫的死信队列,前面会有截图专门给大家看。
绑定后的成果,在我的项目启动后 RabbitMQ 会把交换机和队列都创立进去,在控制台就能看到。
一般队列绑定死信交换机和对应的路由规定后,咱们接下来就把死信交换机、路由规定、队列创立进去即可,其实和创立一般队列没区别。
<br/>
4、yml 配置
为了演示不便,咱们的生产者和消费者是写在同一个我的项目中的,所以配置文件没有区别。然而在线上环境中,为理解耦生产者和消费者往往是离开的。
这里能够发现,咱们给 RabbitMQ 开启了音讯确认机制,读过结尾提过那篇文章的小伙伴应该晓得,线上环境咱们为了进步性能个别是不关上确认机制的,这里之所以关上,是为了演示音讯的投递状况,同时也为了特地讲前面提早插件会呈现的一个问题。
<br/>
5、创立生产者
这里加了诸如音讯惟一 ID、音讯确认机制的写法,单纯为了展现给大家看,实际上你能够不加。
<br/>
6、创立消费者
这里留神,监听的队列也就是咱们后面申明的死信队列,因为过期的音讯都通过绑定的死信交换机转发到了外面,如果对过程有纳闷,能够回到结尾的图解那里对着图片来看。
<br/>
7、测试接口
别离创立了 5s 提早和 15min 提早的测试接口
<br/>
8、成果
<br/>
提早插件形式
插件形式,要比死信队列形式简略得多,只须要装置插件,启动插件性能,而后创立提早队列即可。
1、装置插件
这里给出源码装置形式和 docker 装置形式,大家依据各自状况本人选。
1)、源码装置
下载插件:https://github.com/rabbitmq/r…
本次演示下载的是 3.8.0 版本插件,它能兼容 3.7.x 和 3.8.x 的 RabbitMQ
直通车:https://github.com/rabbitmq/r…
揭示一下,要下载和 RabbitMQ 对应的版本,否则提早队列不会失效。这外面有些版本是兼容低版本 MQ 的,能够点击进去具体查看反对的版本。
将下载好的插件上传到 RabbitMQ 的 plugins 下:rabbitmq_server-3.7.24/plugins
而后开启提早队列:rabbitmq-plugins enable rabbitmq_delayed_message_exchange
这样就能够开始欢快的应用啦,具体的操作和图示在上面的 docker 装置步骤里展现进去,这俩的操作没啥区别。
2)、docker 装置
首先,还是一样的步骤,把下载好的插件上传到 RabbitMQ 服务器上,因为是 docker 形式,所以你还得把它上传到 docker 容器中。
复制文件到 docker 容器中:
进入容器查看是否上传胜利
开启提早插件,开启胜利后就是上面的提醒成果。
重启 RabbitMQ,不重启不会失效,如果重启没成果,你可能须要 kill 过程再启动。
关上控制台界面,如果看到创立交换机的选项中有了 x -delayed-message,就示意提早插件装置和启动胜利。
<br/>
2、yml 配置
这里我专门画了红框,是因为这个配置在提早插件中有一个作用,能够加也能够不加,前面的踩坑手记会独自解释。
<br/>
3、申明交换机和队列
这里只需注意一点,交换机创立时要设置为提早交换机,也就是setDelayed(true)。
<br/>
4、音讯处理器工具类
咱们通过 MessagePostProcessor 这个钩子来设置长久化模式和延迟时间,我的项目中可能多个中央会用到,所以独自抽取进去通过工具类获取对象。
<br/>
5、创立生产者
这个写法是固定的,能够间接复制到本人的我的项目中应用,延迟时间本人定义。
<br/>
6、创立消费者
间接监听这个提早队列即可,没有特地的中央。
<br/>
7、测试接口
这里咱们测试这个 6s 提早的音讯是否胜利
<br/>
8、成果
能够看到,刚好 6s 后生产了音讯,提早成果没问题。
<br/>
踩坑手记
1、死信队列坑点
其实死信队列的坑点比插件要少多了,然而死信队列没有插件那么简略间接好了解。
1)、原理肯定要弄明确,否则你连交换机和队列怎么创立怎么绑定都不晓得;
2)、绑定死信交换机时,x-dead-letter 这些参数对应的值肯定要写对,尤其是路由,写错了会导致过期音讯不进入死信队列,找半天起因都找不到;
<br/>
2、提早插件坑点
提早插件的坑点就真的多了,笔者用死信队列形式简直是 1 次就胜利,插件反倒折腾了我好几个回合。
1)、提早插件的版本肯定要下载对,和 RabbitMQ 自身版本对应,最好是点进去看下阐明,个别都会通知你兼容哪个版本,如果下载错了,我只能为你默哀;
2)、不要下载过高版本的 RabbitMQ 和插件,你可能会疯掉;
3)、开启插件后肯定要重启 RabbitMQ,否则不失效,如果重启也没失效,你能够尝试下 kill 掉 MQ 的过程,而后再启动;
4)、开启音讯确认机制后,你会发现提早插件很非凡的一个中央,就是每次投递音讯都会进入 returncallback 回调中。
我着重来阐明下最初一个坑点,也谈不上是坑点,只是你当前应用过程中很可能会产生纳闷。
还记得前文中 yml 文件画一个红框的配置吗,咱们把那个参数正文掉看看成果。
你会发现,一旦开启了音讯确认机制,提早音讯每次都会先进入 returncallback 回调,而后才会投递胜利,你们能够本人试一试,每次都会这样。
起因是什么呢?
这里就要提到提早插件的原理了,从前文创立提早交换机那里就能够看到,是给交换机设置了 delayed:true,因为提早音讯实际上是挂载到交换机上,不会马上就通过路由投递进来。
那么咱们再来看看,下面这个图中 returncallback 回调打印进去的返回信息:replyText=NO_ROUTE,很显著了吧,说是没有路由,所以音讯确认失败了,因为提早插件没有马上通过路由投递。
那又有疑难了,没有马上投递,为什么会进入 returncallback 回调呢?
上面这张图是源码中的一段,通知你了为什么。
因为有个 mandatory 参数,如果不配置它的话,它是为 null 的,当为 null 的时候,传递的值是this.properties.isPublisherReturns()。
官网对 mandatory 的解释如下:
Enable mandatory messages. If a mandatory message cannot be routed to a queue by the server, it will return an unroutable message with a Return method.
翻译过去:
开启强制性的音讯。如果强制音讯不能被服务器路由到队列,它将应用 return 办法返回一个不可路由的音讯。
所以,mandatory 为 true 示意开启强制投递,为 false 示意不强制,而且这个值能够为 null。
而咱们后面 yml 文件中开启了音讯确认机制:publisher-returns: true
所以,每次肯定会走 returncallback 回调。
因而,咱们 要么不开启音讯确认机制,要么就把 mandatory 设置为 false,这样提早插件的音讯就不会每次走一遍回调了。
<br/>
最佳应用心得
1)、提早插件形式更不便,但我倡议首选死信队列形式,因为死信队列在 RabbitMQ 的应用中占比还挺重的,它不仅能够用在提早音讯,还能够用在其余很多中央,另外死信队列是必学的,并且拿来即用免去了装置插件带来的危险;
2)、死信队列在提早音讯这块其实是有隐形 BUG 的,它在多个延迟时间场景同时存在的状况下有先后执行程序的问题,可能呈现 15min 的提早音讯在后面,导致 5s 的提早音讯要期待 15min 的执行完了再执行,这是 RabbitMQ 自身的有序机制导致的,只对队尾音讯断定,所以咱们在应用时肯定要做提早音讯隔离,不同提早场景要离开解决;
3)、提早插件就没有上述死信队列的问题,曾经专门解决了简单场景下的有序问题,所以一个我的项目在开始之初决定要应用 RabbitMQ 的时候,不论将来用不用得上,请务必就在装置时顺便也把提早插件也装上,防止我的项目中期突然想用时装置插件必须重启 RabbitMQ 带来的未知危险;
4)、大部分状况下,死信队列齐全足够,但切记我的项目中不要频繁应用提早队列,基本上用到的场景会很少,比方定时关单、定时开释锁资源等等,特定的对延迟时间准确性要求很高的场景才用,其余还是以分布式任务调度为主,提早队列太多会引起不必要的认知凌乱;
5)、我集体的教训,提早音讯的场景下,交换机最好应用 Direct 点对点模式,咱们公司已经呈现过共事应用 Topic 模式,自认为路由规定匹配写的没问题,实际上导致规定抵触音讯被其余消费者给生产掉了,MQ 流转音讯自身是静默解决的,所以他找死都找不到起因,直到起初被其余共事偶尔发现才解决。所以对于这种繁多场景最保险的形式还是 Direct,一对一总不会有问题。
<br/>
总结
总结下来就以下几点:
1)、不论用不必,在装置 RabbitMQ 时就顺便把提早插件也装上;
2)、举荐以死信队列形式为主;
3)、不要太多中央应用提早队列;
4)、交换机模式应用 Direct 点对点。
最初,我会把本次案例的代码地址放在评论中,两种实现形式都有,能够间接运行起来,想要学习的能够下载来看看。
<br/>
原创文章纯手打,感觉有一滴滴帮忙就请举手之劳点个 赞和 珍藏 吧~
继续分享工作中的实在教训和心得体会,喜爱的话就点个 关注 吧~
更多最新技术文章可关注 GZH:【Java 分享客栈】