RabiitMQ延迟队列(死信交换机)

2024-02-07 09:12

本文主要是介绍RabiitMQ延迟队列(死信交换机),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Dead Letter Exchange(死信交换机)

        在MQ中,当消息成为死信(Dead message 死掉的信息)后,消息中间件可以将其从当前队列发送到另一个队列中,这个队列就是死信队列。而 在RabbitMQ中,由于有交换机的概念,实际是将死信发送给了死信交换机(Dead Letter Exchange,简称DLX)。死信交换机和死信队列和普通的没有区别。

消息成为死信的情况

  1. 队列消息长度到达限制
  2. 消费者拒签消息,并且不把消息重新放入原队列
  3. 消息到达存活时间未被消费

有些队列的消息成为死信后,(比如过期了或者队列满了)这些死信一般情况下是会被 RabbitMQ 清理的。但是你可以配置某个交换机为此队列的死信交换机,该队列的消息成为死信后会被重新发送到此 DLX 。至于怎么处理这个DLX中的死信就是看具体的业务场景了,DLX 中的信息可以被路由到新的队列。

  • 生产者 

    /*** 普通交换机绑定普通交换机** @return*/@Beanpublic Queue queueA() {//信息配置Map<String, Object> map = new HashMap<>();//message在该队列queue的存活时间最大为15秒map.put("x-message-ttl", 15000);//x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)map.put("x-dead-letter-exchange", "exchangeB");//x-dead-letter-routing-key参数是给这个DLX指定路由键map.put("x-dead-letter-routing-key", "queueB");return new Queue("queueA", true, false, false, map);}@Beanpublic DirectExchange exchangeA() {return new DirectExchange("exchangeA");}@Beanpublic Binding bindingA() {return BindingBuilder.bind(queueA()).to(exchangeA()).with("queueA");}/*** 死信交换机绑定死信交换机** @return*/@Beanpublic Queue queueB() {return new Queue("queueB");}@Beanpublic DirectExchange exchangeB() {return new DirectExchange("exchangeB");}@Beanpublic Binding bindingB() {return BindingBuilder.bind(queueB()).to(exchangeB()).with("queueB");}
  •  模拟发送请求
    @RequestMapping("/send6")public String sendSix() throws JsonProcessingException {rabbitTemplate.convertAndSend("exchangeA", "queueA", "检查订单是否过期");return "🫶";}

这时我发送请求到队列queueA,并设置了15秒的延迟,将超时的信息调用到死信交换机中。在这里我是没开启消费者所有没有消费者去处理该请求的,信息在queueA队列等待15秒后将会转到死信交换机queueB队列进行处理:

延迟队列

  • 延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。经典的应用场景是下单减库存。 

        根据以上结论,在rabbitmq中消费者只要接到信息就会自动确认进行处理。所以在上面并没有开启消费者,当请求時效后(如订单未支付,定时30分钟自动取消功能)我们不应该再让它正常处理,而把该请求放到死信交换机中安排对应的处理,所以我们需要打消费者自动处理请求改成手动。 

  • 如果手动确认则当消费者调用 ack、nack、reject 几种方法进行确认,手动确认可以在业务失败后进行一些操作,如果消息未被 ACK 则会发送到下一个消费者

  • 如果某个服务忘记 ACK 了,则 RabbitMQ 不会再发送数据给它,因为 RabbitMQ 认为该服务的处理能力有限

  • ACK 机制还可以起到限流作用,比如在接收到某条消息时休眠几秒钟

  • 消息确认模式有:

  1. AcknowledgeMode.NONE:自动确认
  2. AcknowledgeMode.AUTO:根据情况确认
  3. AcknowledgeMode.MANUAL:手动确认

确认消息(局部方法处理消息)

  • 默认情况下消息消费者是自动 ack (确认)消息的,如果要手动 ack(确认)则需要修改确认模式为 manual

消费者添加手动确认消息配置配置 :

spring:rabbitmq:listener:simple:acknowledge-mode: manua
  • 消费者接受消息: 
package com.ycxw.consumer.demos;import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;import java.io.IOException;/*** @author 云村小威* @create 2024-01-25 15:36*/
@Component
public class DLXReceiver {@RabbitListener(queues = {"queueA"})@RabbitHandlerpublic void handlerA(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {System.out.println("已接受到队列queueA传递过来的消息:" + msg);channel.basicReject(tag, false);// 拒接消息,如果为true则拒绝后又从新回到队列被接受(循环),除非消息过期。//channel.basicAck(tag, true); 确认消息()一次性全接受,如果为false则接受一次}/*** 接受死信消息** @param msg*/@RabbitListener(queues = {"queueB"})@RabbitHandlerpublic void handlerB(String msg) {/*** ...接受到信息,去数据库处理*/System.out.println("已接受到队列queueB传递过来的消息:" + msg);}
}

 第一次进入普通队列别拒绝后,转到死信队列中处理...

需要注意的 basicAck 方法需要传递两个参数

  • deliveryTag(唯一标识 ID):当一个消费者向 RabbitMQ 注册后,会建立起一个 Channel ,RabbitMQ 会用 basic.deliver 方法向消费者推送消息,这个方法携带了一个 delivery tag, 它代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID,是一个单调递增的正整数,delivery tag 的范围仅限于 Channel

  • multiple:为了减少网络流量,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息

这篇关于RabiitMQ延迟队列(死信交换机)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/687239

相关文章

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has

最便宜的8口2.5G网管交换机! 水星SE109 Pro拆机测评

《最便宜的8口2.5G网管交换机!水星SE109Pro拆机测评》水星SE109Pro价格很便宜,水星SE109Pro,外观、接口,和SE109一样,区别Pro是网管型的,下面我们就来看看详细拆... 听说水星SE109 Pro开卖了,PDD卖 220元,于是买回来javascript拆机看看。推荐阅读:水

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

hdu1180(广搜+优先队列)

此题要求最少到达目标点T的最短时间,所以我选择了广度优先搜索,并且要用到优先队列。 另外此题注意点较多,比如说可以在某个点停留,我wa了好多两次,就是因为忽略了这一点,然后参考了大神的思想,然后经过反复修改才AC的 这是我的代码 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<

poj 3190 优先队列+贪心

题意: 有n头牛,分别给他们挤奶的时间。 然后每头牛挤奶的时候都要在一个stall里面,并且每个stall每次只能占用一头牛。 问最少需要多少个stall,并输出每头牛所在的stall。 e.g 样例: INPUT: 51 102 43 65 84 7 OUTPUT: 412324 HINT: Explanation of the s

poj 2431 poj 3253 优先队列的运用

poj 2431: 题意: 一条路起点为0, 终点为l。 卡车初始时在0点,并且有p升油,假设油箱无限大。 给n个加油站,每个加油站距离终点 l 距离为 x[i],可以加的油量为fuel[i]。 问最少加几次油可以到达终点,若不能到达,输出-1。 解析: 《挑战程序设计竞赛》: “在卡车开往终点的途中,只有在加油站才可以加油。但是,如果认为“在到达加油站i时,就获得了一

poj3750约瑟夫环,循环队列

Description 有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。 Input 第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用