RabbitMQ 如何使用延迟队列

发布于:2024-05-13 ⋅ 阅读:(42) ⋅ 点赞:(0)

RabbitMQ 如何使用延迟队列

目录

  1. 前置条件
  2. 场景描述
  3. RabbitMQ 延迟队列机制
  4. 实现步骤
  5. 示例代码
  6. 注意事项

前置条件

  • 操作系统: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 延迟队列插件

# 下载插件
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 类型的交换机,并设定延迟队列,用于处理延迟消息。

# 创建交换机
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 来指定延迟时间。

# 使用 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 项目中,可以通过以下配置来创建延迟交换机和队列。

@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 代码

@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 代码

@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();
        }
    }
}

注意事项

  1. 插件兼容性:请确保 rabbitmq_delayed_message_exchange 插件与您的 RabbitMQ 版本兼容,否则可能导致插件无法加载。
  2. Erlang 版本:RabbitMQ 依赖于 Erlang,因此确保您的 Erlang 版本满足 RabbitMQ 版本的最低要求。
  3. 延迟时间限制:合理设置延迟时间,避免消息被延迟过长时间导致系统不可预测的性能问题。