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. 时效性较差 | 高可靠性、长延迟场景 |
最佳实践建议
- 短延迟(秒/分钟级):使用延迟交换机插件
- 固定延迟:使用TTL+死信队列
- 长延迟(小时/天级):使用数据库+定时任务
- 高精度延迟:结合Redis有序集合
yaml
# Spring Boot 配置示例
spring:
rabbitmq:
host: localhost
port: 5672
# 延迟交换机插件支持
rabbitmq:
delayed:
exchange:
enabled: true
选择方案时需考虑:延迟精度、消息量、系统复杂度、是否需要持久化等因素。