共计 3265 个字符,预计需要花费 9 分钟才能阅读完成。
简介 本文介绍 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 分钟后收到音讯,判断该订单的状态是否为已领取,若还没领取,则将该订单状态设置为:已勾销。定时遥控场景:用户想用手机近程遥控家里的智能设施在指定的工夫工作。
计划:假如用户想要的操作是:开启热水器。首先,将开启热水器这个音讯发送到提早队列,延迟时间设置到用户想要的工夫到当初工夫的差值。开启热水器这个音讯的订阅者程序在指定工夫收到音讯,再将指令推送到智能设施。
须要留神的是,提早队列的音讯是不能取消的,解决方案是:在生产音讯的时候判断这个音讯对应的业务的以后状态。例如:对于勾销订单来说,收到音讯时,读取这个音讯所对应的数据库信息,如果曾经是已付款状态了,就不进行任何操作了,如果是未领取状态,则改为已勾销。