前言

前段时间写过一篇:

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