1. 死信队列概念

    死信队列(Dead Letter Exchange),死信交换器。当业务队列中的消息被拒绝或者过期或者超过队列的最大长度时,消息会被丢弃,但若是配置了死信队列,那么消息可以被重新发布到另一个交换器,这个交换器就是DLX,与DLX绑定的队列称为死信队列。

若业务队列想绑定死信队列,那么在声明业务队列时,需要指定DLX(死信Exchange)和DLK(死信RoutingKey)。

控制台.png

2.消息成为"死信"的前提

消息被否定确认,使用 channel.basicNack或channel.basicReject ,并且此时requeue属性被设置为false。

消息在队列的存活时间超过设置的TTL时间。

消息队列的消息数量已经超过最大队列长度。

“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。

  1. 如何为业务队列配置死信队列

    在声明业务队列时,指定死信配置。

@Configuration

public class RabbitMqConfig {

/**
* 死信队列 交换机标识符
*/
public static final String DEAD_LETTER_QUEUE_KEY = "x-dead-letter-exchange";
/**
* 死信队列交换机绑定键标识符
*/
public static final String DEAD_LETTER_ROUTING_KEY = "x-dead-letter-routing-key"; /********************************************************
* 创建死信队列
*******************************************************/ public final static String deadQueueName = "dead_queue";
public final static String deadRoutingKey = "dead_routing_key";
public final static String deadExchangeName = "dead_exchange"; /**
* 创建死信队列
*/
@Bean
public Queue deadQueue() {
return new Queue(deadQueueName, true);
} /**
* 创建死信交换机
*/
@Bean
public DirectExchange deadExchange() {
return new DirectExchange(deadExchangeName);
} /**
* 死信队列与死信交换机绑定
*/
@Bean
public Binding bindingDeadExchange(Queue deadQueue, DirectExchange deadExchange) {
return BindingBuilder.bind(deadQueue).to(deadExchange).with(deadRoutingKey);
} /*************************************************
* 创建业务队列
************************************************/
public final static String kinsonQueueName = "kinson_queue";
public final static String kinsonRoutingKey = "kinson_routing_key";
public final static String kinsonExchangeName = "kinson_exchange"; /**
* 创建业务交换机
*/
@Bean
public DirectExchange kinsonExchange() {
return new DirectExchange(kinsonExchangeName);
} /**
* 创建业务队列时——声明了死信队列
*/
@Bean
public Queue kinsonQueue() {
// 将普通队列绑定到死信队列交换机上
Map<String, Object> args = new HashMap<>(2);
args.put(DEAD_LETTER_QUEUE_KEY, deadExchangeName);
args.put(DEAD_LETTER_ROUTING_KEY, deadRoutingKey);
return new Queue(kinsonQueueName, true, false, false, args);
} /**
* 绑定关系
*/
@Bean
public Binding kinsonRoutingKey(Queue kinsonQueue, DirectExchange kinsonExchange) {
return BindingBuilder.bind(kinsonQueue).to(kinsonExchange).with(kinsonRoutingKey);
}

}

死信队列并不是特殊的队列,只是绑定到死信交换机上的队列。死信交换机只是接受死信的普通交换机,它的类型也是[Direct、Fanout、Topic]。一般来说,会给每一个业务队列都声明一个独有的路由key,并对应配置一个死信队列进行监听(但是一个项目可共用一个死信交换机)。

  1. 死信消息的Header

    @Component

    @Slf4j

    public class CustomerRev {

    //业务队列

    @RabbitListener(queues = {"kinson_queue"})

    public void receiver(Message msg, Channel channel) {

    try {

    //打印数据

    String message = new String(msg.getBody(), StandardCharsets.UTF_8);

    log.info("【业务队列msg.getMessageProperties().getHeaders()】:{}",JSON.toJSONString(msg.getMessageProperties().getHeaders()));

    //(手动确认)丢弃消息,且不重新回队列,消息会进入死信队列

    channel.basicReject(msg.getMessageProperties().getDeliveryTag(), false);

    } catch (Exception e) {

    log.error("错误信息:{}", e.getMessage());

    }

    }

    //死信队列

    @RabbitListener(queues = {"dead_queue"})

    public void receiver2(Message msg, Channel channel) {

    try {

    //打印数据

    log.info("【死信队列msg.getMessageProperties().getHeaders():{}】", JSON.toJSONString(msg.getMessageProperties().getHeaders()));

    log.info("【死信队列msg.getMessageProperties().getXDeathHeader():{}】", JSON.toJSONString(msg.getMessageProperties().getXDeathHeader()));

    String message = new String(msg.getBody(), StandardCharsets.UTF_8);

    log.info("死信队列收取消息:"+message);

    channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);

    } catch (Exception e) {

    log.error("错误信息:{}", e.getMessage());

    }

    }

    }

    执行消息:

【业务队列msg.getMessageProperties().getHeaders()】:{}

【死信队列msg.getMessageProperties().getHeaders():{"x-first-death-exchange":"","x-death":[{"reason":"rejected","count":1,"exchange":"","time":1604549067000,"routing-keys":["kinson_queue"],"queue":"kinson_queue"}],"x-first-death-reason":"rejected","x-first-death-queue":"kinson_queue"}】

【死信队列msg.getMessageProperties().getXDeathHeader():[{"reason":"rejected","count":1,"exchange":"","time":1604549067000,"routing-keys":["kinson_queue"],"queue":"kinson_queue"}]】

死信队列收取消息:Date:1604549064079

死信队列消费的消息的Header:

字段名 含义

x-first-death-exchange 第一次被抛入的死信交换机的名称

x-first-death-reason 第一次成为死信的原因,rejected:消息在重新进入队列时被队列拒绝,由于default-requeue-rejected参数被设置为false。expired :消息过期。maxlen: 队列内消息数量超过队列最大容量

x-first-death-queue 第一次成为死信前所在队列名称

x-death 历次被投入死信交换机的信息列表,同一个消息每次进入一个死信交换机,这个数组的信息就会被更新

5. 死信队列使用场景

在较为重要的业务场景中,确保未被消费的消息不被丢弃,一般发送消息异常可能原因主要有消息本身存在错误导致业务处理异常,参数校验异常,网络波动导致查询或调用接口异常。为了不使消息堆积,从而丢弃消息。

当发生异常时,不能每次通过日志来获取原消息,处理完问题后重新投递消息。通过配置死信队列,可以让未正确消费的消息暂存到死信队列中,后续排查清楚问题,编写相应的代码来处理死信消息(不在手动的恢复数据)。

推荐阅读

一文了解死信队列

【RabbitMQ-7】RabbitMQ—交换机标识符的更多相关文章

  1. 【RabbitMQ学习之二】RabbitMQ四种交换机模式应用

    环境 win7 rabbitmq-server-3.7.17 Erlang 22.1 一.概念1.队列队列用于临时存储消息和转发消息.队列类型有两种,即时队列和延时队列. 即时队列:队列中的消息会被立 ...

  2. .NET 云原生架构师训练营(模块二 基础巩固 RabbitMQ 工作队列和交换机)--学习笔记

    2.6.4 RabbitMQ -- 工作队列和交换机 WorkQueue Publish/Subscribe Routing EmitLog WorkQueue WorkQueue:https://w ...

  3. 初识RabbitMQ,附RabbitMQ+PHP演示实例

    RabbitMQ是一个在AMQP基础上实现的企业级消息系统.何谓消息系统,就是消息队列系统,消息队列是""消费-生产者模型""的一个典型的代表,一端往消息队列中 ...

  4. 快速掌握RabbitMQ(一)——RabbitMQ的基本概念、安装和C#驱动

    1 RabbitMQ简介 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现,官网地址:http://www.rabbitmq.com.Ra ...

  5. 【RabbitMQ】RabbitMQ的一些基础概念

    工作中使用的是RabbitMQ,需要对其进行熟悉.使用之前,弄清楚它是什么东西,解决什么问题. 场景 一些不必实时执行的任务 开发中,有一些任务并无须实时执行,比如: 会员更新个人信息,更新会员信息之 ...

  6. 我为什么要选择RabbitMQ ,RabbitMQ简介,各种MQ选型对比(转载)

    转载自:https://www.sojson.com/blog/48.html 前言: MQ 是什么?队列是什么,MQ 我们可以理解为消息队列,队列我们可以理解为管道.以管道的方式做消息传递. 场景: ...

  7. 【RabbitMQ】 RabbitMQ配置开机启动

    环境 系统:Linux(CentOS 7.2) Erlang环境:21.1(安装参考[Erlang]源码安装) RabbitMQ:3.7.9(安装参考[RabbitMQ] RabbitMQ安装) 配置 ...

  8. 为什么要选择RabbitMQ ,RabbitMQ简介,各种MQ选型对比

    原文:https://www.sojson.com/blog/48.html 前言: MQ 是什么?队列是什么,MQ 我们可以理解为消息队列,队列我们可以理解为管道.以管道的方式做消息传递. 场景: ...

  9. 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门

    [详细][转]C#中理解委托和事件   文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...

随机推荐

  1. firewalld和iptables区别

    在RHEL7里有几种防火墙共存:firewalld.iptables.ebtables,默认是使用firewalld来管理netfilter子系统,不过底层调用的命令仍然是iptables等. fir ...

  2. 多测师讲解自动化测试 _RFalert弹框._高级讲师肖sir

    alert弹框定位 Open Browser file:///D:\\bao\\baoan\\alert弹框.html gc sleep 2 Handle Alert accept #点击确定 Han ...

  3. set的运用 例题5-3 安迪的第一个字典(Andy's First Dictionary,Uva 10815)

    #include<bits/stdc++.h>using namespace std;set<string> dict;int main(){ string s, buf; w ...

  4. html学习(2)

    标签的语义化,也就是标签的用途. html.css.javascript作用: HTML是网页内容的载体.内容就是网页制作者放在页面上想要让用户浏览的信息,可以包含文字.图片.视频等. CSS样式是表 ...

  5. 无法访问GitHub

    我们开发者经常用的最大的同性交流平台--GitHub忽然访问不了了,很尴尬 可以打开控制台 ping一下 github.com 果不其然 不通 不过幸运的是里面有github的ip地址,好像是美国某个 ...

  6. html中object标签

    首先将这个强大web页面打印例子(pintTest.html)贴上来. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional ...

  7. JS实现鼠标移入水波效果

    前言 最近比较沉迷JS,所以我现在来做个鼠标的交互效果 HTML <div style="border-radius;position:relative;width:800px;hei ...

  8. python3异步爬虫 ——aiohttp模板使用

    一.简单使用和讲解 import aiohttp import asyncio async def fetch(client): async with client.get('http://httpb ...

  9. scott lock

    账户被锁: cmd --->sqlplus /nolog--->conn sys/change_on_install as sysdba;---->alter user scott ...

  10. 如何解决json返回的乱码

    方法一: 通过@RequestMaping的produces属性来实现,修改下代码 //produces:指定响应体返回类型和编码@RequestMapping(value = "/xxx& ...