乐趣区

关于java:RabbitMQ延迟消息死信队列-延迟插件-二合一用法踩坑手记最佳使用心得

前言

前段时间写过一篇:

[# 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 分享客栈】

退出移动版