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. 手写一个HTTP框架:两个类实现基本的IoC功能

    jsoncat: 仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架 国庆节的时候,我就已经把 jsoncat 的 IoC 功能给写了,具体可以看这篇文章&l ...

  2. 洛谷 P4819 [中山市选]杀人游戏(tarjan缩点)

    P4819 [中山市选]杀人游戏 思路分析 题意最开始理解错了(我太菜了) 把题意简化一下,就是找到可以确定杀手身份的最小的危险查看数 (就是不知道该村名的身份,查看他的身份具有危险的查看数量),用 ...

  3. C/C++编程日记:用C语言实现的简单Web服务器(Linux),全代码分享!

    相信大家对Apache都有所听闻,Apache是目前使用最为广泛我Web服务器.大家可以从news.netcraft.com/这个网站得到证实. 这是腾讯的uptime.netcraft.com/up ...

  4. 19。删除链表倒数第N个节点

    class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next# 这道题还是很简单的,我们只 ...

  5. selenium自动登陆

    import osfrom selenium import webdriverimport time,jsonclass Cookie(object): def __init__(self,drive ...

  6. Halcon软件介绍与图像基本知识

    1.halcon环境 halcon功能:1.视觉算法(核心)基本 2. 弱语言 3.解释性语言 halcon软件介绍: 1.标题栏 2.菜单栏 3.工具栏 4.工作区 图形窗口(显示图像) 变量窗口( ...

  7. C# 清除文本中的HTML标签

    /// <summary>          /// 清除文本中Html的标签          /// </summary>          /// <param n ...

  8. readcf: option RunAsUser: unknown user smmsp发送邮件失败问题

    今天使用mail命令发送邮件时,发送不了,错误信息如下: /etc/mail/submit.cf: line 432: readcf: option RunAsUser: unknown user s ...

  9. javac -d的意思是?

  10. frida框架hook获取方法输出参数(常用于简单的so输出参数获取,快速开发)

    一.模板 function douyinencode(data) { var result = {}; Java.perform(function () { try { var Test = Java ...