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. 搭建单机版的kafka

    搭建单机版的kafka  

  2. English 介词

    English 介词 Create Time : 2019-06-27 表示时间的介词称为时间介词.表示时间的介词有:at, on, in, before, after等. 一.at, on和in ① ...

  3. MeteoInfoLab脚本示例:SeaWiFS HDF Grid数据

    SeaWiFS HDF Grid数据读取,特别是涉及到了文件的众多属性数据的读取,数据取对数后绘图.脚本程序: #Add data file f = addfile('D:/Temp/hdf/S199 ...

  4. Go net/http包

    net/http包 net/http是Go语言的内置包,它可以来创建HTTP客户端与服务端. 并且由net/http包创建的服务端性能十分高效,甚至不用nginx部署. client端 GET请求 以 ...

  5. C语言的污垢,一个能污染内存的神秘操作!神级坑位再现~

    本文目的是为了更好的理解指针和内存管理 背景 我们定义一个变量A,修改另外一个一个变量B,导致A的值被修改,我们称它为内存污染. 案例 如下程序,正常的预期输出应该是:97 98 256 ,但正确的结 ...

  6. property和setter装饰器

    # property装饰器 # 作用: 将一个get方法转换为对象的属性. 就是 调用方法改为调用对象 # 使用条件: 必须和属性名一样 # setter方法的装饰器: # 作用:将一个set方法转换 ...

  7. 第二十二章 Nginx性能优化

    一.性能优化概述 1.我们需要了解 1.首先需要了解我们当前系统的结构和瓶颈,了解当前使用的是什么,运行的是什么业务,都有哪些服务,了解每个服务最大能支撑多少并发.比如nginx作为静态资源服务并发是 ...

  8. 某次burp抓包出错的解决办法

    前些日子同事发微信问我一个问题 没听懂他说的没回显是啥意思,于是叫他把站发给我. 浏览器不挂burp代理能正常打开,挂上burp代理以后浏览器显示连接超时 首先测试burp能抓其他的包应不是这个原因 ...

  9. Linux入门到放弃之六《磁盘和文件系统管理一》

    要求:创建卷组名为 mail_store:逻辑卷名 mail,从卷组mail_store上划出50GB空间, 使用mkfs命令创建ext3文件系统,并实现开机自动挂载,挂载点/mailbox: (1) ...

  10. 虚拟机之kali2.0

    kali2.0下载地址: 链接: https://pan.baidu.com/s/1CiQJNfvUPFw3aJR103XSbg 提取码: 57wp