RabbitMQ 如何使用延迟队列
目录
- 前置条件
 - 场景描述
 - [RabbitMQ 延迟队列机制](#RabbitMQ 延迟队列机制)
 - 实现步骤
- [1. 安装 RabbitMQ 延迟队列插件](#1. 安装 RabbitMQ 延迟队列插件)
 - [2. 创建延迟队列和交换机](#2. 创建延迟队列和交换机)
 - [3. 发布延迟消息](#3. 发布延迟消息)
 - [4. 消费延迟消息](#4. 消费延迟消息)
 
 - 示例代码
- [1. 延迟队列配置](#1. 延迟队列配置)
 - [2. 发布消息的 Producer 代码](#2. 发布消息的 Producer 代码)
 - [3. 消费消息的 Consumer 代码](#3. 消费消息的 Consumer 代码)
 
 - 注意事项
 
前置条件
- 操作系统:CentOS 7
 - RabbitMQ:版本 3.8.0+
 - Erlang:版本 21.0+
 - RabbitMQ 延迟队列插件:
rabbitmq_delayed_message_exchange 
场景描述
假设我们正在设计一个线上售卖电影票的系统,用户购票后有 15 分钟时间进行付款,如果用户在 15 分钟内未付款,订单将自动取消并释放电影票库存。这里,我们可以利用 RabbitMQ 的延迟队列机制,在用户购票时发送一条延迟消息到 RabbitMQ,并设定延迟时间为 15 分钟。如果用户未在 15 分钟内完成付款,延迟消息将被消费者接收并处理订单取消的逻辑。
RabbitMQ 延迟队列机制
RabbitMQ 本身不直接支持延迟队列功能,需要借助 rabbitmq_delayed_message_exchange 插件来实现。该插件为 RabbitMQ 提供了一种新的消息交换机类型------x-delayed-message,可以基于消息属性设置延迟时间,在设定的延迟时间后,将消息发送到目标队列。
实现步骤
1. 安装 RabbitMQ 延迟队列插件
            
            
              bash
              
              
            
          
          # 下载插件
wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.8.0/rabbitmq_delayed_message_exchange-3.8.0.ez
# 将插件移动到 RabbitMQ 插件目录
mv rabbitmq_delayed_message_exchange-3.8.0.ez /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.0/plugins/
# 启用插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
        2. 创建延迟队列和交换机
我们将使用 x-delayed-message 类型的交换机,并设定延迟队列,用于处理延迟消息。
            
            
              bash
              
              
            
          
          # 创建交换机
rabbitmqadmin declare exchange name=delayed_exchange type=x-delayed-message \
    arguments='{"x-delayed-type":"direct"}'
# 创建队列
rabbitmqadmin declare queue name=delayed_queue
# 绑定交换机与队列
rabbitmqadmin declare binding source=delayed_exchange destination=delayed_queue routing_key=order.payment
        3. 发布延迟消息
在发布消息时,可以设置消息属性 x-delay 来指定延迟时间。
            
            
              bash
              
              
            
          
          # 使用 rabbitmqadmin 发布延迟消息
rabbitmqadmin publish exchange=delayed_exchange routing_key=order.payment \
    payload="{'order_id': '12345', 'status': 'PENDING_PAYMENT'}" \
    properties='{"headers":{"x-delay":900000}}'
        4. 消费延迟消息
消费者将从延迟队列中消费消息并执行订单取消逻辑。
示例代码
1. 延迟队列配置
在 Spring Boot 项目中,可以通过以下配置来创建延迟交换机和队列。
            
            
              java
              
              
            
          
          @Configuration
public class RabbitConfig {
    public static final String DELAYED_EXCHANGE_NAME = "delayed_exchange";
    public static final String DELAYED_QUEUE_NAME = "delayed_queue";
    public static final String ROUTING_KEY = "order.payment";
    // 创建延迟交换机
    @Bean
    public CustomExchange delayedExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }
    // 创建延迟队列
    @Bean
    public Queue delayedQueue() {
        return new Queue(DELAYED_QUEUE_NAME);
    }
    // 绑定延迟队列与交换机
    @Bean
    public Binding delayedBinding(Queue delayedQueue, CustomExchange delayedExchange) {
        return BindingBuilder.bind(delayedQueue).to(delayedExchange).with(ROUTING_KEY).noargs();
    }
}
        2. 发布消息的 Producer 代码
            
            
              java
              
              
            
          
          @Component
public class OrderProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendOrderMessage(String orderId) {
        Map<String, Object> message = new HashMap<>();
        message.put("order_id", orderId);
        message.put("status", "PENDING_PAYMENT");
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("x-delay", 15 * 60 * 1000); // 延迟 15 分钟
        Message msg = new Message(new ObjectMapper().writeValueAsBytes(message), messageProperties);
        rabbitTemplate.convertAndSend(RabbitConfig.DELAYED_EXCHANGE_NAME, RabbitConfig.ROUTING_KEY, msg);
    }
}
        3. 消费消息的 Consumer 代码
            
            
              java
              
              
            
          
          @Component
public class OrderConsumer {
    @RabbitListener(queues = RabbitConfig.DELAYED_QUEUE_NAME)
    public void processOrderCancellation(Message message) {
        try {
            Map<String, Object> orderMessage = new ObjectMapper().readValue(message.getBody(), Map.class);
            String orderId = (String) orderMessage.get("order_id");
            // 取消订单逻辑
            System.out.println("Order " + orderId + " has been canceled due to non-payment.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
        注意事项
- 插件兼容性 :请确保 
rabbitmq_delayed_message_exchange插件与您的 RabbitMQ 版本兼容,否则可能导致插件无法加载。 - Erlang 版本:RabbitMQ 依赖于 Erlang,因此确保您的 Erlang 版本满足 RabbitMQ 版本的最低要求。
 - 延迟时间限制:合理设置延迟时间,避免消息被延迟过长时间导致系统不可预测的性能问题。