【面试题】RabbitMQ 怎么实现延迟队列?

RabbitMQ 实现延迟队列主要有以下几种方式:

1. TTL + 死信队列(最常用)

原理

  • 设置消息或队列的 TTL(Time-To-Live)
  • 消息过期后成为死信,转发到死信队列
  • 消费者从死信队列获取延迟消息

实现步骤

java 复制代码
// 1. 创建死信交换机
channel.exchangeDeclare("dlx.exchange", "direct", true);

// 2. 创建死信队列
Map<String, Object> dlxArgs = new HashMap<>();
dlxArgs.put("x-dead-letter-exchange", "dlx.exchange");
dlxArgs.put("x-dead-letter-routing-key", "dlx.routingKey");

// 3. 创建延迟队列(设置TTL)
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 10000); // 10秒TTL
args.put("x-dead-letter-exchange", "dlx.exchange");
args.put("x-dead-letter-routing-key", "dlx.routingKey");
channel.queueDeclare("delay.queue", true, false, false, args);

// 4. 绑定
channel.queueBind("delay.queue", "normal.exchange", "normal.routingKey");
channel.queueBind("dlx.queue", "dlx.exchange", "dlx.routingKey");

2. rabbitmq-delayed-message-exchange 插件(推荐)

安装插件

bash 复制代码
# 下载插件(版本需匹配RabbitMQ版本)
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

使用示例

java 复制代码
// 1. 声明延迟交换机
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare(
    "delayed.exchange", 
    "x-delayed-message", 
    true, false, args
);

// 2. 发送延迟消息
Map<String, Object> headers = new HashMap<>();
headers.put("x-delay", 5000); // 延迟5秒

AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder();
props.headers(headers);

channel.basicPublish(
    "delayed.exchange", 
    "routing.key", 
    props.build(), 
    message.getBytes()
);

3. 消息级别 TTL

java 复制代码
// 为单条消息设置TTL
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .expiration("10000") // 10秒后过期
    .build();

channel.basicPublish(
    "exchange", 
    "routingKey", 
    properties, 
    message.getBytes()
);

4. 定时轮询数据库方案

java 复制代码
// 将延迟消息存入数据库
public void sendDelayedMessage(Message message, long delaySeconds) {
    // 计算执行时间
    Date executeTime = new Date(System.currentTimeMillis() + delaySeconds * 1000);
    // 存入数据库
    delayMessageRepository.save(message, executeTime);
    
    // 定时任务轮询
    @Scheduled(fixedDelay = 5000)
    public void processDelayedMessages() {
        List<Message> readyMessages = delayMessageRepository
            .findReadyMessages(new Date());
        for (Message msg : readyMessages) {
            // 发送到RabbitMQ
            rabbitTemplate.convertAndSend(msg);
        }
    }
}

对比总结

方案 优点 缺点 适用场景
TTL+死信队列 无需插件,RabbitMQ原生支持 1. 队列只能设置固定延迟时间 2. 消息级别TTL可能产生队头阻塞 固定延迟时间的场景
延迟交换机插件 1. 每条消息可单独设置延迟 2. 无队头阻塞问题 需要安装插件 灵活延迟、精确控制
数据库方案 1. 延迟时间可持久化 2. 支持复杂调度 1. 依赖外部存储 2. 时效性较差 高可靠性、长延迟场景

最佳实践建议

  1. 短延迟(秒/分钟级):使用延迟交换机插件
  2. 固定延迟:使用TTL+死信队列
  3. 长延迟(小时/天级):使用数据库+定时任务
  4. 高精度延迟:结合Redis有序集合
yaml 复制代码
# Spring Boot 配置示例
spring:
  rabbitmq:
    host: localhost
    port: 5672
  # 延迟交换机插件支持
  rabbitmq:
    delayed:
      exchange:
        enabled: true

选择方案时需考虑:延迟精度、消息量、系统复杂度、是否需要持久化等因素。

相关推荐
有梦想的攻城狮1 个月前
Rabbitmq在死信队列中的队头阻塞问题
分布式·rabbitmq·死信队列·延迟队列
在未来等你5 个月前
RabbitMQ面试精讲 Day 8:死信队列与延迟队列实现
消息队列·rabbitmq·死信队列·延迟队列·分布式系统·面试技巧
快乐肚皮7 个月前
Redisson学习专栏(四):实战应用(分布式会话管理,延迟队列)
分布式·学习·redisson·延迟队列·分布式会话
龙哥·三年风水10 个月前
麒麟操作系统-rabbitmq二进制安装
消息队列·延迟队列·麒麟
宇宙超级勇猛无敌暴龙战神1 年前
基于redis完成延迟队列
数据库·redis·缓存·redisson·延迟队列
Micro麦可乐2 年前
Spring Boot整合Redis通过Zset数据类型+定时任务实现延迟队列
spring boot·redis·后端·线程池·延迟队列·zset数据类型
青石路2 年前
RabbitMQ 进阶使用之延迟队列 → 订单在30分钟之内未支付则自动取消
rabbitmq·延迟队列
橡 皮 人2 年前
Redis第18讲——Redis和Redission实现延迟消息
redis·面试·延迟队列·redission
谢小涛2 年前
Spring Boot集成Redisson实现延迟队列
spring boot·redis·redisson·延迟队列·延迟消息