RabbitMQ 死信队列

文章目录


前言

消息过期以后,如果没有任何配置,是会直接丢弃的。我们可以通过配置让这样的消息变成死信(Dead Letter),在别的地方存储。

1、死信交换机 DLX 与死信队列 DLQ

队列在创建的时候可以指定一个死信交换机 DLX(Dead Letter Exchange)。死信交换机绑定的队列被称为死信队列 DLQ(Dead Letter Queue)。

DLX 实际上也是普通的交换机,DLQ 也是普通的队列,只是他有死信的特殊功能。(例如替补球员也是普通球员,只不过他有替补的功能)。

如果消息过期了,队列指定了DLX,就会发送到 DLX。如果 DLX 绑定了 DLQ,就会路由到 DLQ。 消费者绑定当前DLQ,消息就会分发到消费者,进行消费。

2、死信队列的实现

2.1、声明原队列信息

声明原交换机(ori_exchange)、原队列(ori_queue),相互绑定,指定原队列的死信交换机为(dlx_exchange)

java 复制代码
Map<String, Object> args = new HashMap<String, Object>();
// 声明 Direct 交换机
args.put("x-dead-letter-exchange", "dlx_exchange");
// args.put("x-dead-letter-routing-key", "dlx_routing_key"); // 可选,默认为消息的原路由键
args.put("x-message-ttl", 60000); // 可选,TTL (单位: ms)
args.put("x-max-length", 1000); // 可选,最大消息数
channel.exchangeDeclare("ori_exchange", "direct", true);
channel.queueDeclare("ori_queue", false, false, false, args);
  • x-dead-letter-exchange:定义了普通队列的 DLX,当消息变为死信时,会被发送到配置的 DLX。
  • x-dead-letter-routing-key:可选参数,定义消息在 DLX 中的路由键。如果未设置,默认使用消息原本的路由键。
  • x-message-ttl:可选参数,设置消息在队列中的 TTL,即消息在队列中存在的最大时间(毫秒)。
  • x-max-length:可选参数,设置队列的最大消息数,超过后会先清理头部消息到死信队列。

注意:如果同时指定了 Message TTL 和 Queue TTL,则小的那个时间生效

2.2、声明死信队列信息

声明死信交换机(DEAD_LETTER_EXCHANGE )、死信队列(DEAD_LETTER_QUEUE),并且通过"#"绑定,代表无条件路由

java 复制代码
channel.exchangeDeclare("dlx_exchange", "direct", true);
channel.queueDeclare("dlx_queue", false, false, false, null);
channel.queueBind("dlx_queue", "dlx_exchange", "dlx_routing_key");
  • dlx_exchange:定义了一个死信交换机。
  • dlx_queue:定义了一个专门用于处理死信消息的队列。
  • queueBind:将死信队列绑定到死信交换机,并指定路由键。

2.3、完整示例

生产者

java 复制代码
public class Producer {
    private final static String ORI_EXCHANGE = "ori_exchange";
    private final static String ORI_QUEUE = "ori_queue";
    private final static String DLX_QUEUE = "dlx_queue";
    private final static String DLX_EXCHANGE = "dlx_exchange";
    private final static String DLX_ROUTING_KEY = "dlx_routing_key";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {

            // Create ori queue with DLX configuration
            Map<String, Object> args = new HashMap<>();
            args.put("x-dead-letter-exchange", DLX_EXCHANGE);
            args.put("x-dead-letter-routing-key", DLX_ROUTING_KEY);
            args.put("x-message-ttl", 60000); // 可选,TTL (单位: ms)
            args.put("x-max-length", 1000); // 可选,最大消息数
            channel.exchangeDeclare(ORI_EXCHANGE, "direct", true);
            channel.queueDeclare(ORI_QUEUE, true, false, false, args);
            channel.queueBind(ORI_QUEUE, ORI_EXCHANGE, "direct");

            // Create DLX and DLX queue
            channel.exchangeDeclare(DLX_EXCHANGE, "direct", true);
            channel.queueDeclare(DLX_QUEUE, true, false, false, null);
            channel.queueBind(DLX_QUEUE, DLX_EXCHANGE, DLX_ROUTING_KEY);

            // Publish a test message to the primary queue
            String message = "Hello, World!";
            channel.basicPublish(ORI_EXCHANGE, ORI_QUEUE, null, message.getBytes());

            System.out.println(" [x] Sent '" + message + "'");

            // Simulate message rejection
            // For example, consume and nack the message
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

消费者

java 复制代码
public class Consumer {
    private final static String DLX_QUEUE = "dlx_queue";
    private final static String DLX_EXCHANGE = "dlx_exchange";
    private final static String DLX_ROUTING_KEY = "dlx_routing_key";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {
            channel.exchangeDeclare(DLX_EXCHANGE, "direct", true);
            channel.queueDeclare(DLX_QUEUE, true, false, false, null);
            channel.queueBind(DLX_QUEUE, DLX_EXCHANGE, DLX_ROUTING_KEY);

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
            };
            channel.basicConsume(DLX_QUEUE, true, deliverCallback, consumerTag -> {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、死信消息流转原理

消息的流转流程:

生产者------原交换机------原队列(超过 TTL 之后、队列超长、消费者拒绝消费)------死信交换机------死信队列------最终消费者

相关推荐
组合缺一11 分钟前
Solon Cloud Gateway 开发:Route 的过滤器与定制
java·后端·gateway·reactor·solon
我是苏苏32 分钟前
C#高级:常用的扩展方法大全
java·windows·c#
customer0836 分钟前
【开源免费】基于SpringBoot+Vue.JS贸易行业crm系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源
_GR1 小时前
Java程序基础⑪Java的异常体系和使用
java·开发语言
CPU NULL1 小时前
新版IDEA创建数据库表
java·数据库·spring boot·sql·学习·mysql·intellij-idea
极客先躯2 小时前
高级java每日一道面试题-2025年01月22日-JVM篇-乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
java·jvm·优化性能·选择合适的锁策略·结合实际案例·乐观锁的实现方式
秋月的私语2 小时前
c#启动程序时使用异步读取输出避免假死
java·前端·c#
花心蝴蝶.2 小时前
Spring IoC & DI
java·后端·spring
Kerwin要坚持日更2 小时前
一文讲解CMS收集器的垃圾收集过程
java·开发语言·jvm
我想学LINUX2 小时前
【2024年华为OD机试】 (C卷,200分)- 机器人走迷宫(JavaScript&Java & Python&C/C++)
java·c语言·javascript·python·华为od·机器人