【RabbitMQ】10 深入部分P3 死信队列(交换机)
1、死信交换机
说是死信队列,是因为RabbitMQ和其他中间件产品不一样
有交换机的概念和这个东西存在,别的产品只有队列一说
DeadLetterExchange
消息成为DeadMessage之后,被重新发往另一个交换机
接收DeadMessage的交换机就成为死信交换机
但是死信的条件还有其他情况:
设置规则:
实现步骤:
1、设立一套正常的消息队列流程
2、设立一套死信交换机的消息队列流程
3、让正常的交换机和死信交换机绑定
Spring生产者服务的xml配置步骤:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/> <!-- 定义rabbitmq connectionFactory publisher-confirms="true" 消息发送可确认
publisher-returns="true"
-->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"
publisher-confirms="true"
publisher-returns="true"
/>
<!--定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/> <!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/> <!--
死信交换机配置步骤
1、配置正常的队列和交换机
2、配置死信交换机和队列
3、将正常交换机和死信交换机配置
- 死信交换机名称 x-dead-letter-exchange
- 发送到死信交换机的路由分配规则 x-dead-letter-routing-key 要测试配置是否成功
需要有消息可以死亡
1、设置队列过期
2、设置队列长度 限流
--> <!-- 1配置正常的队列和交换机 -->
<rabbit:queue id="test-dlx-normal-queue" name="test-dlx-normal-queue" >
<!-- 3、绑定死信队列交换机 -->
<rabbit:queue-arguments>
<!-- 3.1绑定交换机 -->
<entry key="x-dead-letter-exchange" value="test-dlx-dead-exchange" />
<!-- 3.2设定死亡分配规则 -->
<entry key="x-dead-letter-routing-key" value="dlx.anyRule"/> <!-- 4.1死亡时限 -->
<entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
<!-- 4.2限流控制 -->
<entry key="x-max-length" value="10" value-type="java.lang.Integer"/> </rabbit:queue-arguments>
</rabbit:queue>
<rabbit:topic-exchange name="test-dlx-normal-exchange" >
<rabbit:bindings>
<rabbit:binding pattern="test.dlx.#" queue="test-dlx-normal-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange> <!-- 2配置死信队列和交换机 -->
<rabbit:queue id="test-dlx-dead-queue" name="test-dlx-dead-queue" >
</rabbit:queue>
<rabbit:topic-exchange name="test-dlx-dead-exchange" >
<rabbit:bindings>
<rabbit:binding pattern="dlx.#" queue="test-dlx-dead-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange> </beans>
测试代码:
/**
* 死信测试
*/
@Test
public void deadExchangeTest() {
rabbitTemplate.convertAndSend(
"test-dlx-normal-exchange",
"test.dlx.anyRule",
" deadExchange queue message test ... ");
}
正常队列可以看到经过了过期时间,消息死亡
死信队列在正常队列的消息死亡后被队列分配进来:
监控面板这里也能查找我们发送的那条消息:
测试长度限制触发死信:
/**
* 死信测试
*/
@Test
public void deadExchangeTest() {
// rabbitTemplate.convertAndSend(
// "test-dlx-normal-exchange",
// "test.dlx.anyRule",
// " deadExchange queue message test ... "); for (int i = 0; i < 15; i++) {
rabbitTemplate.convertAndSend(
"test-dlx-normal-exchange",
"test.dlx.anyRule",
" deadExchange queue message test ... ");
} }
前面的10条进入正常队列
后面5条满列了,只能被分配死信里面
再到时间过期,剩下10条也进去了
后面的5条因为满列了,分配到死信
时间过期,入列的10条也进去了
看死信的曲线图也是这样的过程
情况三,绝收:
编写消费者服务的死信监听器类
复制Acknowledge监听器做了一些调整,异常不打印出来,一堆爆红省略
package cn.dzz.rabbitmq.listener; import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; /**
* 手动确认配置:
* 1、监听器容器配置项acknowledge更改为manual
* <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual">
* <rabbit:listener ref="acknowledgeListener" queue-names="confirm-test-queue" />
* </rabbit:listener-container>
*
* 2、监听器改用实现ChannelAwareMessageListener接口
*
* 3、消息处理成功 调用basicAck()签收响应
* 4、处理失败 调用basicNack()签收拒绝 broker重新发送
*
*
*/
@Component
public class DeadExchangeListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = 0;
Thread.sleep(2000); // 让消息别那么快的一直发送,慢点发 try { System.out.println(new String(message.getBody(), StandardCharsets.UTF_8)); // 处理业务逻辑
// todo... int i = 10 / 0; // 这个异常会被捕获,然后触发RabbitMQ一直让消息重新入列发送 // 业务签收
deliveryTag = message.getMessageProperties().getDeliveryTag(); channel.basicAck(deliveryTag, true); } catch (Exception exception) {
// exception.printStackTrace();
System.out.println("已经触发异常Catch 消息被拒绝 .... ");
/**
* public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException {
* long realTag = deliveryTag - this.activeDeliveryTagOffset;
* if (multiple && deliveryTag == 0L) {
* realTag = 0L;
* } else if (realTag <= 0L) {
* return;
* }
*
* this.transmit(new Nack(realTag, multiple, requeue));
* this.metricsCollector.basicNack(this, deliveryTag);
* }
* long deliveryTag 签收的标签
* boolean multiple 允许签收多条消息?
* boolean requeue 是否重回队列? 消息重新入列?RabbitMQ重新发送
*/
channel.basicNack(deliveryTag, true, false); // 被拒绝的消息不再重回队列, 这样这个消息才会分配到死信队列中 /**
* 和上面区别就是没有多消息签收的参数
* public void basicReject(long deliveryTag, boolean requeue) throws IOException {
* long realTag = deliveryTag - this.activeDeliveryTagOffset;
* if (realTag > 0L) {
* this.transmit(new Reject(realTag, requeue));
* this.metricsCollector.basicReject(this, deliveryTag);
* }
*
* }
* channel.basicReject(deliveryTag, true);
*/ }
}
}
监听器容器注册进来:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/> <!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/> <context:component-scan base-package="cn.dzz.rabbitmq.listener" /> <!--
定义监听器容器
acknowledge="manual" 默认就是none
-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual">
<!-- 监听的是正常的队列 -->
<rabbit:listener ref="deadExchangeListener" queue-names="test-dlx-normal-queue" />
</rabbit:listener-container> </beans>
监听测试:
监听服务就是一直跑着收消息就行:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:consumer-config.xml")
public class SpringConsumerTest { @Test
public void simpleListenerTest() {
System.out.println("监听测试");
while(true) { }
}
}
生产者来发送一条消息:
/**
* 死信测试
*/
@Test
public void deadExchangeTest() {
// rabbitTemplate.convertAndSend(
// "test-dlx-normal-exchange",
// "test.dlx.anyRule",
// " deadExchange queue message test ... "); // for (int i = 0; i < 15; i++) {
// rabbitTemplate.convertAndSend(
// "test-dlx-normal-exchange",
// "test.dlx.anyRule",
// " deadExchange queue message test ... ");
// }
rabbitTemplate.convertAndSend(
"test-dlx-normal-exchange",
"test.dlx.anyRule",
"消息拒绝测试 。。。。 在死信队列查看此消息 "); }
先启动消费者监听,然后生产者发送
会看到消息在正常队列中存活了一下,就被分配到死信队列中了
消费者输出信息:
监听测试
消息拒绝测试 。。。。 在死信队列查看此消息
已经触发异常Catch 消息被拒绝 ....
【RabbitMQ】10 深入部分P3 死信队列(交换机)的更多相关文章
- 【RabbitMQ 实战指南】一 死信队列
1.死信队列 DLX,全称为 Dead-Letter-Exchange,可以称之为死信交换器.当消息在一个队列中变成死信(dead message)之后,它能被发送到另一个交换器中,这个交换器就是DL ...
- rabbitmq系列(四)死信队列
一.什么是死信队列 当消息在一个队列中变成一个死信之后,它将被重新publish到另一个交换机上,这个交换机我们就叫做死信交换机,私信交换机将死信投递到一个队列上就是死信队列.具体原理如下图: 消息变 ...
- 消息队列RabbitMQ(五):死信队列与延迟队列
死信队列 引言 死信队列,英文缩写:DLX .Dead Letter Exchange(死信交换机),其实应该叫做死信交换机才更恰当. 当消息成为Dead message后,可以被重新发送到另一个交换 ...
- 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?
哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...
- Rabbitmq消费失败死信队列
Rabbitmq 重消费处理 一 处理流程图: 业务交换机:正常接收发送者,发送过来的消息,交换机类型topic AE交换机: 当业务交换机无法根据指定的routingkey去路由到队列的时候,会全部 ...
- RabbitMQ死信队列
关于RabbitMQ死信队列 死信队列 听上去像 消息“死”了 其实也有点这个意思,死信队列 是 当消息在一个队列 因为下列原因: 消息被拒绝(basic.reject/ basic.nac ...
- RabbitMQ配置死信队列
死信队列 消息传输过程中难免会产生一些无法及时处理的消息,这些暂时无法处理的消息有时候也是需要被保留下来的,于是这些无法被及时处理的消息就变成了死信. 既然需要保留这些死信,那么就需要一个容器来存储它 ...
- RabbitMq死信队列(接盘侠)
队列创建之后,后期对其修改或者参数添加会报错.需要把队列重新删除,重新创建线上环境不能把队列删除,优雅安全的方式是重新建一个队列,把死信队列相关的队列进行绑定 在有过期时间的队列中设定最大接收能力5条 ...
- 【RabbitMQ 实战指南】一 延迟队列
1.什么是延迟队列 延迟队列中存储延迟消息,延迟消息是指当消息被发送到队列中不会立即消费,而是等待一段时间后再消费该消息. 延迟队列很多应用场景,一个典型的应用场景是订单未支付超时取消,用户下单之后3 ...
- RabbitMQ死信队列另类用法之复合死信
前言 在业务开发过程中,我们常常需要做一些定时任务,这些任务一般用来做监控或者清理任务,比如在订单的业务场景中,用户在创建订单后一段时间内,没有完成支付,系统将自动取消该订单,并将库存返回到商品中,又 ...
随机推荐
- MySQL 导出一条数据的插入语句
1.MySQL 导出一条数据的插入语句的方法 在MySQL中,如果我们想要导出一条数据的插入语句,我们可以使用SELECT ... INTO OUTFILE语句(但这通常用于将整个表或查询结果导出到一 ...
- 如何排查常规软件问题 - 面向 Linux 初级用户的教程
笔者从 14 年做开源软件以来,接触了众多 Linux 新手用户,这里我为这类用户总结了一些常见的问题排查方法,希望能帮助到大家.如果你已经工作多年,对于下面提到的思路和方法应该非常熟悉,如果对某一条 ...
- 为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的.而 Mybatis 在查询关联对象或关联集合 ...
- mapperTemp
@Insert("INSERT INTO coxisolate.instanceinfo (instance_id, app_name, create_time, update_time, ...
- 用pm2命令管理你的node项目
文章目录 前言 安装 运行项目 pm2的命令 前言 我在服务器上运行node项目,使用命令nohup npm start &,结果关闭终端之后,进程就会停止,看来nohup也不是万能的后台运行 ...
- IoTBrowser V2.0:引领物联网时代的全新浏览器
强大的兼容性,无限的可能 IoTBrowser V2.0,基于Chromium内核,完美支持H5/css/js开发界面,让您的物联网应用拥有与主流浏览器同等的流畅体验.同时,它还支持CSS 3动画.C ...
- python 无监督生成模型
无监督生成模型在机器学习中扮演着重要角色,特别是当我们在没有标签数据的情况下想要生成新的样本或理解数据的内在结构时.一种流行的无监督生成模型是生成对抗网络(Generative Adversarial ...
- 【简单总结】SLAM 算法的 Benchmark 及相关数据集的结果对比
前言与参考 主要是copy一下总结,方便自己后续找方案特定使用,所有的出处均在标题处和原链接跳转,此处仅做各个benchmark收集使用,如果有原作者觉得侵权,请联系我 将全力配合相关内容和链接删除 ...
- 【Error】mysql的error.log中ranges: 268 max_threads: 4 split: 268 depth: 2是什么意思?
2021-12-08T09:36:39.612332+08:00 44213799 [Note] [MY-011825] [InnoDB] Parallel scan: 4 2021-12-08T09 ...
- InvocationTargetException和UndeclaredThrowableException异常介绍
今天来介绍了两个陌生又熟悉的异常类,熟悉是因为我们经常会遇到它们,陌生是好像又从来不知道它们是做什么的 假定读者已经清楚了Java的异常分类: 一是程序不能处理的错误(Error), 二是程序应该避免 ...