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 死信队列(交换机)的更多相关文章

  1. 【RabbitMQ 实战指南】一 死信队列

    1.死信队列 DLX,全称为 Dead-Letter-Exchange,可以称之为死信交换器.当消息在一个队列中变成死信(dead message)之后,它能被发送到另一个交换器中,这个交换器就是DL ...

  2. rabbitmq系列(四)死信队列

    一.什么是死信队列 当消息在一个队列中变成一个死信之后,它将被重新publish到另一个交换机上,这个交换机我们就叫做死信交换机,私信交换机将死信投递到一个队列上就是死信队列.具体原理如下图: 消息变 ...

  3. 消息队列RabbitMQ(五):死信队列与延迟队列

    死信队列 引言 死信队列,英文缩写:DLX .Dead Letter Exchange(死信交换机),其实应该叫做死信交换机才更恰当. 当消息成为Dead message后,可以被重新发送到另一个交换 ...

  4. 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...

  5. Rabbitmq消费失败死信队列

    Rabbitmq 重消费处理 一 处理流程图: 业务交换机:正常接收发送者,发送过来的消息,交换机类型topic AE交换机: 当业务交换机无法根据指定的routingkey去路由到队列的时候,会全部 ...

  6. RabbitMQ死信队列

    关于RabbitMQ死信队列 死信队列 听上去像 消息“死”了     其实也有点这个意思,死信队列  是 当消息在一个队列 因为下列原因: 消息被拒绝(basic.reject/ basic.nac ...

  7. RabbitMQ配置死信队列

    死信队列 消息传输过程中难免会产生一些无法及时处理的消息,这些暂时无法处理的消息有时候也是需要被保留下来的,于是这些无法被及时处理的消息就变成了死信. 既然需要保留这些死信,那么就需要一个容器来存储它 ...

  8. RabbitMq死信队列(接盘侠)

    队列创建之后,后期对其修改或者参数添加会报错.需要把队列重新删除,重新创建线上环境不能把队列删除,优雅安全的方式是重新建一个队列,把死信队列相关的队列进行绑定 在有过期时间的队列中设定最大接收能力5条 ...

  9. 【RabbitMQ 实战指南】一 延迟队列

    1.什么是延迟队列 延迟队列中存储延迟消息,延迟消息是指当消息被发送到队列中不会立即消费,而是等待一段时间后再消费该消息. 延迟队列很多应用场景,一个典型的应用场景是订单未支付超时取消,用户下单之后3 ...

  10. RabbitMQ死信队列另类用法之复合死信

    前言 在业务开发过程中,我们常常需要做一些定时任务,这些任务一般用来做监控或者清理任务,比如在订单的业务场景中,用户在创建订单后一段时间内,没有完成支付,系统将自动取消该订单,并将库存返回到商品中,又 ...

随机推荐

  1. set数据类型

                SET 数据类型             是一个类似于 数组 的数据类型             特点 : 不接受重复的相同的数据                    同样的 ...

  2. session 和 cookie 有什么区别?

    a.存储位置不同:session 存储在服务器端:cookie 存储在浏览器端. b.安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改. c.容量和个数限制:cookie 有容量限 ...

  3. jenkins发布服务失败查看catalina.out启动日志和xxl-job jobhandler naming conflicts

    jenkins发布服务失败查看catalina.out启动日志和xxl-job jobhandler naming conflicts 1.查看tomcat/logs/catalina.out 日志, ...

  4. es创建索引及别名更新mapping方法 elasticsearch [nested] nested object under path [XXX] is not of nested type

    [nested] nested object under path [XXX] is not of nested type这是因为在创建索引时没有指定类型为数组,这就是一个大坑,ES官方说可以不用指定 ...

  5. Hibernate Validator 校验注解

    Hibernate Validator 校验注解/** * 认识一些校验注解Hibernate Validator * * @NotNull 值不能为空 * @Null 值必须为空 * @Patter ...

  6. WebUI自动化测试中关于图片验证码的解决方法

    关于怎么识别图片中的文字,传送门:https://www.cnblogs.com/teangtang/p/16157880.html 实现代码如下: #! /usr/bin/env python # ...

  7. Django项目实现分页返回,结合forloop实现编号递增

    需要导入Paginator包 from django.core.paginator import Paginator 实现步骤: 需要设置每页大小 需要获取每页的页码, 查询对应的数据,提供给Pagi ...

  8. PPP协议简介

    转载出处:https://blog.csdn.net/csucxcc/article/details/1684416 PPP(Point-to-Point Protocol)协议是在SLIP的基础上发 ...

  9. FFMpeg 中的数据结构

    FFMpeg 中比较重要的函数以及数据结构如下: 数据结构: (1) AVFormatContext (2) AVOutputFormat (3) AVInputFormat (4) AVCodecC ...

  10. 【ClickHouse】7:clickhouse多实例安装

    背景介绍: 有三台CentOS7服务器安装了ClickHouse HostName IP 安装程序 实例1端口 实例2端口 centf8118.sharding1.db 192.168.81.18 c ...