RabbitMQ(二)、死信队列(Dead Letter)

本文详细介绍了RabbitMQ中的死信队列概念及其工作原理,包括死信交换机的作用、消息成为死信的情况及死信处理流程。同时提供了具体的实现步骤,如依赖引入、配置类定义、消费端与生产端的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是死信交换机和死信队列

死信交换机:"死信"的消息经由一个交换机到达另一个队列,这样用于处理”死信“的交换机就是死信交换机(dead-letter-exchange,DLX)

死信队列:与死信交换机绑定的队列就是死信队列。“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。

消息什么时候变成死信

“死信”是RabbitMQ中的一种消息机制,当你在消费消息时,如果队列里的消息出现以下三种情况,那么该消息将成为“死信”。

  • 消息在队列的存活时间超过设置的TTL时间(如果队列和消息都是设置了TTL,将以最小的为主)
  • 消息被否定确认,使用 channel.basicNackchannel.basicReject ,并且此时 requeue 属性被设置为 false
  • 消息队列的消息数量已经超过最大队列长度/最大大小限制;

死信处理过程

  • DLX 也是一个正常的 Exchange,和一般的 Exchange 没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性(x-dead-letter-exchange)。

  • 当这个队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的 dead-letter-exchange 上去,进而被路由到死信队列。

  • 死信队列对应的消费端可以监听这个队列中的消息做相应的处理。

死信队列如何使用

1. 引入依赖

<dependency>
	<groupId>com.rabbitmq</groupId>
	<artifactId>amqp-client</artifactId>
	<version>5.7.3</version>
</dependency>

2. 创建配置类

public class RabbitMQConfig {
    private RabbitMQConfig() {
    }

    // 主机IP
    public static final String HOST = "127.0.0.1";
    // 主机port
    public static final Integer PORT = 5672;
    // 主机port
    public static final String VHOST = "/";
    // 主机port
    public static final String USERNAME = "admin";
    // 主机port
    public static final String PASSWORD = "admin";
    // 交换机名称
    public static final String DIRECT_EXCHANGE = "direct_exchange";
    // 队列名称
    public static final String QUEUE_NAME = "direct_queue";
    // Routing key
    public static final String ROUTING_KEY = "kiss";

    // 死信交换机名称
    public static final String DEAD_LETTER_EXCHANGE = "DLX_exchange";
    // 死信队列名称
    public static final String DEAD_LETTER_QUEUE = "DLX_queue";
    // 死信交换机和死信队列 Routing key, # 代表所有消息
    public static final String DEAD_LETTER_ROUTING_KEY = "#";
}

3. 消费端代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * 消费者
 */
public class MessageConsumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        // 设置主机IP
        factory.setHost(RabbitMQConfig.HOST);
        // 设置端口
        factory.setPort(RabbitMQConfig.PORT);
        // 设置 Vhost
        factory.setVirtualHost(RabbitMQConfig.VHOST);

        // 设置访问用户
        factory.setUsername(RabbitMQConfig.USERNAME);
        factory.setPassword(RabbitMQConfig.PASSWORD);
        // 建立连接
        Connection connection = factory.newConnection();
        // 创建Channel消息通道
        Channel channel = connection.createChannel();

        /**
         * 声明交换机,参数String exchange, String type, boolean durable, boolean autoDelete, Map<String, Object> arguments
         *
         * String exchange:指定交换机名称
         * String type:路由类型,direct、topic、fanout
         * boolean durable:是否持久化
         * boolean autoDelete:是否自动删除
         * Map<String, Object> arguments:其他参数
         */
        channel.exchangeDeclare(RabbitMQConfig.DIRECT_EXCHANGE, "direct", false, false, null);

        // 设置队列参数:x-dead-letter-exchange为指定当前队列的死信交换机
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", RabbitMQConfig.DEAD_LETTER_EXCHANGE);
        /**
         * 声明队列,参数String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
         * String queue:指定队列名称
         * boolean durable:是否持久化
         * boolean exclusive:是否排他,既是否创建者私有,如果为true,会对当前队列加锁,其他通道不能访问,并且
         * 					 在连接关闭时会自动删除,不受持久化和自动删除限制
         * boolean autoDelete:是否自动删除
         * Map<String, Object> arguments:其他参数
         */
        channel.queueDeclare(RabbitMQConfig.QUEUE_NAME, false, false, false, arguments);

        /**
         * 绑定交换和队列,参数String queue, String exchange, String routingKey, Map<String, Object> arguments
         */
        channel.queueBind(RabbitMQConfig.QUEUE_NAME, RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.ROUTING_KEY, null);

        /*********************声明死信交换机和死信队列,并绑定*************************/
        channel.exchangeDeclare(RabbitMQConfig.DEAD_LETTER_EXCHANGE, "topic", false, false, null);
        channel.queueDeclare(RabbitMQConfig.DEAD_LETTER_QUEUE, false, false, false, null);
        channel.queueBind(RabbitMQConfig.DEAD_LETTER_QUEUE, RabbitMQConfig.DEAD_LETTER_EXCHANGE,
                RabbitMQConfig.DEAD_LETTER_ROUTING_KEY);

        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 获取消息
                String msg = new String(body, "UTF-8");
                System.out.println("consumer message:" + msg);
            }
        };

        // 开始获取消息String queue, boolean autoAck, Consumer callback
        // channel.basicConsume(RabbitMQConfig.QUEUE_NAME, true, consumer);
    }
}

4. 生产端代码

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * 生产者
 */
public class MessageProvider {
    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        // 设置主机IP
        factory.setHost(RabbitMQConfig.HOST);
        // 设置端口
        factory.setPort(RabbitMQConfig.PORT);
        // 设置 Vhost
        factory.setVirtualHost(RabbitMQConfig.VHOST);

        // 设置访问用户
        factory.setUsername(RabbitMQConfig.USERNAME);
        factory.setPassword(RabbitMQConfig.PASSWORD);
        try (
                // 建立连接
                Connection connection = factory.newConnection();
                // 创建Channel消息通道
                Channel channel = connection.createChannel();
        ) {
            // 设置属性,消息5秒后过期
            AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .deliveryMode(2)// 持久化消息
                    .contentEncoding("UTF-8")
                    .expiration("5000")// TTL-Time to live
                    .build();

            String msg = "阁下可是常山赵子龙";
            channel.basicPublish(RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.ROUTING_KEY, properties, msg.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
### 配置与使用RabbitMQ中的死信队列 #### 1. Maven依赖导入 为了在项目中集成RabbitMQ并支持死信队列功能,需引入`spring-boot-starter-amqp`作为依赖项。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 此依赖提供了访问AMQP协议的支持以及Spring AMQP抽象层的功能[^1]。 #### 2. RabbitMQ连接配置 通过修改application.yml文件来设置RabbitMQ服务器的相关参数: ```yaml spring: rabbitmq: host: 83.136.16.134 port: 5672 username: guest password: guest listener: simple: acknowledge-mode: manual # 手动确认模式 default-requeue-rejected: false # 不重新入队被拒绝的消息 ``` 上述配置指定了RabbitMQ服务端地址、认证信息及监听器行为选项。其中手动应答机制(`acknowledge-mode`)对于处理失败重试逻辑至关重要;而关闭自动重排队列(`default-requeue-rejected=false`)则防止了因消费者异常而导致消息无限循环的问题。 #### 3. 死信队列的定义与绑定 创建一个专门用于接收过期或未成功消费的消息的死信交换机(DLX),并将目标业务队列与其关联起来。具体操作如下所示: - **声明DLX**: 定义一个直连型(dead-letter-exchange)或其他类型的交换机实例; - **设定策略属性**: 对于每一个可能产生死信的目标队列,在其声明时指定两个重要的头字段——`x-dead-letter-exchange`(指向DLX名称)`x-dead-letter-routing-key`(可选,指定转发给哪个路由键下的队列)。 以下是Java代码片段展示如何利用Spring Boot框架完成这些步骤: ```java @Configuration public class RabbitConfig { @Bean public Queue dlqQueue() { return new Queue("dlq.queue", true); } @Bean DirectExchange deadLetterExchange() { return new DirectExchange("my.deadletter.exchange"); } @Bean Binding bindingDead(Queue queue, DirectExchange direct){ Map<String,Object> args=new HashMap<>(); args.put("x-dead-letter-exchange","my.deadletter.exchange"); // 设置死信exchange args.put("x-message-ttl",1000); // 可选:设置TTL时间单位ms return BindingBuilder.bind(queue).to(direct).with("normal.routing.key").noargs(args); } } ``` 这段程序首先建立了名为`dlq.queue`的实际存储死信数据的目的地,并设置了相应的死信交换机`my.deadletter.exchange`。接着为正常工作的业务队列绑定了特定的路由规则的同时也赋予了一些额外参数用来指示哪些情况下应该触发向死信队列发送副本的行为。 #### 4. 处理来自死信队列的消息 最后一步就是编写对应的Listener组件去监控和响应那些进入到了死信队列里的内容。这通常涉及到解析原始消息体及其元数据(比如为什么它成为了死信),进而采取适当的动作如记录日志、通知相关人员或是尝试再次投递给其他地方继续流转下去。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值