简介 本文介绍RabbitMQ的死信队列和提早队列。

本内容也是Java后端面试中常见的问题。

死信队列

简介

DLX,全称为Dead-Letter-Exchange,能够称之为死信交换器,也有人称之为死信邮箱。当音讯在一个队列中变成死信(dead message)之后,它能被从新被发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列。

以下几种状况会导致音讯变成死信:

音讯被回绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false;

音讯过期;

队列达到最大长度。

DLX是一个失常的交换器,和个别的交换器没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。当这个队列中存在死信时,RabbitMQ就会主动地将这个音讯从新公布到设置的DLX下来,进而被路由到另一个队列,即死信队列。能够监听这个队列中的音讯以进行相应的解决,这个个性与将音讯的TTL设置为0配合应用能够补救immediate参数的性能。

为队列增加DLX的办法

法1:代码形式

//创立 DLX: dlx_exchange channel.exchangeDeclare("dlx_exchange", "direct" ); Map<String, Object> args = new HashMap<String, Object>; args.put("x-dead-letter-exchange", "dlx_exchange"); //为队列myqueue增加DLX channel.queueDeclare("myqueue", false, false, false, args); 也能够为这个DLX指定路由键。(如果没有非凡指定,则应用原队列的路由键)

args.put("x-dead-letter-routing-key","dlx-routing-key"); 法2:命令形式

rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"dlx_exchange"}' --apply-to queues 示例 代码

channel.exchangeDeclare("exchange.dlx", "direct", true); channel.exchangeDeclare("exchange.normal", "fanout", true); Map<String, Object> args = new HashMap<String, Object>(); args.put("x-message-ttl", 10000); args.put("x-dead-letter-exchange" , "exchange.dlx"); args.put("x-dead-letter-routing-key" , "routingkey"); channel.queueDeclare("queue.normal" , true, false, false, args); channel.queueBind("queue.normal", "exchange.normal", ""); channel.queueDeclare("queue.dlx", true, false, false, null); channel.queueBind("queue.dlx", "exchange.dlx" , "routingkey"); channel.basicPublish("exchange.normal" , "rk", MessageProperties.PERSISTENT_TEXT_PLAIN, "dlx".getBytes());

这里创立了两个交换器exchange.normal和exchange.dlx,别离绑定两个队列queue.normal和queue.dlx。

Web治理页面后果

由下图(图1-1)的Web治理页面能够看出,两个队列都被标记了“D”,这个是durable的缩写,即设置了队列长久化。queue.normal这个队列还配置了TTL、DLX和DLK,其中DLX指的是 x-dead-letter-routing-key这个属性。

案例剖析

参考下图(图1-2),生产者首先发送一条携带路由键为“rk”的音讯,而后通过交换器exchange.normal顺利地存储到队列queue.normal中。因为队列queue.normal设置了过期工夫为10s,在这10s内没有消费者生产这条音讯,那么断定这条音讯为过期。因为设置了DLX,过期之时,音讯被丢给交换器exchange.dlx中,这时找到与exchange.dlx匹配的队列queue.dlx,最初音讯被存储在queue.dk这个死信队列中。

对于RabbitMQ来说,DLX是一个十分有用的个性。它能够解决异常情况下,音讯不可能被消费者正确生产(消费者调用了Basic.Nack或者Basic.Reject)而被置入死信队列中 的状况,后续分析程序能够通过生产这个死信队列中的内容来剖析过后所遇到的异常情况,进而能够改善和优化零碎。DLX配合TTL应用还能够实现提早队列的性能,具体请看下一节。大数据培训

提早队列

简介

提早队列用来寄存提早音讯。提早音讯:指当音讯被发送当前,不想让消费者立即拿到音讯,而是期待特定工夫后,消费者能力拿到这个音讯进行生产。

在AMQP协定中,或者RabbitMQ自身没有间接反对提早队列的性能,然而有两种计划来间接实现:

计划1:采纳rabbitmq-delayed-message-exchange 插件实现。(RabbitMQ 3.6.x开始反对)

计划2:通过后面所介绍的DLX和TTL模拟出提早队列的性能。

在图1-2中,不仅展现的是死信队列的用法,也是提早队列的用法,对于queue.dlx这个死信队列来说,同样能够看作提早队列。假如一个利用中须要将每条音讯都设置为10秒的提早, 生产者通过exchange.normal这个交换器将发送的音讯存储在queue.normal这个队列中。消费者订阅的并非是queue.normal这个队列,而是queue.dlx这个队列。当音讯从queue.normal这个队列中过期之后被存入queue.dlx这个队列中,消费者就凑巧生产到了提早10秒的这条音讯。

在实在利用中,对于提早队列能够依据延迟时间的长短分为多个等级,个别分为5秒、10秒、30秒、1分钟、5分钟、10分钟、30分钟、1小时这几个维度,当然也能够再细化一下。

以下图(图2-1)为例进行阐明。为简化,只设置5秒、10秒、30秒、1分钟这四个等级。依据需要的不同,生产者发送音讯的时候通过设置不同的路由键,将音讯发送到与交换器绑定的不同的队列中。这里队列也别离配置了DLX和相应的死信队列,当相应的音讯过期时,就会转存到相应的死信队列(即提早队列)中,这样消费者依据业务本身的状况,别离抉择不同提早等级的提早队列进行生产。

应用场景

提早队列的应用场景有很多,比方:

用户下订单场景:用户下单后有30分钟的工夫领取,若30分钟内没有领取,则将这个订单勾销。 计划:用户下单后将勾销订单的音讯发送到提早队列,延迟时间设置为30分钟。勾销订单这个音讯的订阅者程序在30分钟后收到音讯,判断该订单的状态是否为已领取,若还没领取,则将该订单状态设置为:已勾销。 定时遥控场景:用户想用手机近程遥控家里的智能设施在指定的工夫工作。

计划:假如用户想要的操作是:开启热水器。首先,将开启热水器这个音讯发送到提早队列,延迟时间设置到用户想要的工夫到当初工夫的差值。开启热水器这个音讯的订阅者程序在指定工夫收到音讯,再将指令推送到智能设施。

须要留神的是,提早队列的音讯是不能取消的,解决方案是:在生产音讯的时候判断这个音讯对应的业务的以后状态。例如:对于勾销订单来说,收到音讯时,读取这个音讯所对应的数据库信息,如果曾经是已付款状态了,就不进行任何操作了,如果是未领取状态,则改为已勾销。