RabbitMQ死信交换机全解析:消息何时会进入死信队列?
RabbitMQ死信交换机全解析:消息何时会进入死信队列?
-
- 1. 什么是死信交换机?
- 2. 消息进入死信交换机的三种情况
-
- 2.1 情况一:消息被消费者拒绝
- 2.2 情况二:消息过期(TTL)
- 2.3 情况三:队列达到最大长度
- 3. 完整配置示例
- 4. 死信消息的原始信息
- 5. 完整流程图
- 6. 实际应用场景
- 7. 常见问题
|
🌺The Begin🌺点点关注,收藏不迷路🌺
|
在RabbitMQ中,死信交换机(DLX)是处理异常消息的核心机制。当消息变成“死信”时,会被自动路由到死信交换机,进而进入死信队列供后续排查或补偿处理。本文将全面解析消息进入死信交换机的三种情况。
1. 什么是死信交换机?
死信指无法被正常消费的消息。消息变成死信后,不会直接被丢弃,而是被发送到预先指定的死信交换机,最终进入死信队列。
消息变成死信
生产者
普通交换机
普通队列
配置了DLX
死信交换机
死信队列
死信消费者
2. 消息进入死信交换机的三种情况
死信来源
消息被拒绝
basic.reject
basic.nack
requeue=false
消息过期
队列TTL
消息TTL
队列达到最大长度
最大消息条数
最大字节数
2.1 情况一:消息被消费者拒绝
消费者调用basic.reject或basic.nack,且requeue=false时,消息进入死信交换机。
@Component
public class MessageConsumer {
@RabbitListener(queues = "normal.queue")
public void handleMessage(Message message, Channel channel) throws IOException {
try {
// 业务处理
processMessage(message);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// ❌ 拒绝且不重新入队 → 进入死信
channel.basicNack(
message.getMessageProperties().getDeliveryTag(),
false, // 不批量
false // requeue = false,不进死信
);
}
}
}
requeue=false与true的区别:
| 参数 | 行为 | 是否进死信 |
|---|---|---|
requeue=true |
重新放回队列尾部 | ❌ |
requeue=false |
进入死信交换机(如配置了DLX) | ✅ |
2.2 情况二:消息过期(TTL)
TTL可通过队列级别或消息级别设置,取较小值。消息过期后被移除队列,进入死信交换机。
队列级别TTL:队列中所有消息统一过期时间
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal.queue")
.ttl(10000) // 10秒过期
.deadLetterExchange("dead.exchange")
.deadLetterRoutingKey("dead.routingkey")
.build();
}
消息级别TTL:每条消息单独设置过期时间
public void sendMessageWithTTL(String message, int ttlMillis) {
MessageProperties props = new MessageProperties();
props.setExpiration(String.valueOf(ttlMillis));
Message msg = MessageBuilder.withBody(message.getBytes())
.andProperties(props)
.build();
rabbitTemplate.convertAndSend("normal.exchange", "normal.key", msg);
}
2.3 情况三:队列达到最大长度
队列设置了最大长度或最大字节数,超出限制时,头部消息变成死信(取决于溢出策略)。
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal.queue")
.maxLength(10) // 最多10条消息
.deadLetterExchange("dead.exchange")
.build();
}
溢出行为默认删除头部消息,这些被删除的消息会进入死信交换机。可通过x-overflow参数配置:drop-head(默认)或reject-publish。
3. 完整配置示例
@Configuration
public class DeadLetterConfig {
public static final String NORMAL_EXCHANGE = "normal.exchange";
public static final String NORMAL_QUEUE = "normal.queue";
public static final String NORMAL_KEY = "normal.key";
public static final String DEAD_EXCHANGE = "dead.exchange";
public static final String DEAD_QUEUE = "dead.queue";
public static final String DEAD_KEY = "dead.key";
@Bean
public DirectExchange normalExchange() {
return new DirectExchange(NORMAL_EXCHANGE);
}
@Bean
public DirectExchange deadExchange() {
return new DirectExchange(DEAD_EXCHANGE);
}
@Bean
public Queue normalQueue() {
return QueueBuilder.durable(NORMAL_QUEUE)
.ttl(10000) // 10秒过期
.maxLength(1000) // 最多1000条
.deadLetterExchange(DEAD_EXCHANGE)
.deadLetterRoutingKey(DEAD_KEY)
.build();
}
@Bean
public Queue deadQueue() {
return QueueBuilder.durable(DEAD_QUEUE).build();
}
@Bean
public Binding normalBinding() {
return BindingBuilder.bind(normalQueue())
.to(normalExchange())
.with(NORMAL_KEY);
}
@Bean
public Binding deadBinding() {
return BindingBuilder.bind(deadQueue())
.to(deadExchange())
.with(DEAD_KEY);
}
}
4. 死信消息的原始信息
消息进入死信队列后,会增加以下Header:
@Component
public class DeadLetterConsumer {
@RabbitListener(queues = "dead.queue")
public void handleDeadLetter(Message message) {
MessageProperties props = message.getMessageProperties();
// 获取死信原因
String reason = props.getHeader("x-death").toString();
// 输出类似:
// [{
// "reason": "rejected", // 死信原因
// "queue": "normal.queue", // 原队列
// "time": 1699876543212, // 死信时间
// "exchange": "normal.exchange",
// "routing-keys": ["normal.key"]
// }]
// 原始消息内容
String originalBody = new String(message.getBody());
// 记录死信,进行补偿处理
saveToDatabase(originalBody, reason);
}
}
死信原因类型:
-
rejected:消费者拒绝且requeue=false -
expired:消息TTL过期 -
maxlen:队列达到最大长度
5. 完整流程图
正常消费
消费者拒绝
requeue=false
消息过期
队列满
头部溢出
消息到达普通队列
消息状态
消费者确认ACK
结束
进入死信交换机
死信交换机路由
死信队列
死信消费者
补偿处理
记录日志/人工介入
6. 实际应用场景
| 场景 | 触发方式 | 处理方案 |
|---|---|---|
| 消息格式错误 | 消费者拒绝 | 进入死信,记录日志,人工修复 |
| 超时未支付订单 | 设置TTL=30分钟 | 进入死信,自动取消订单 |
| 流量高峰积压 | 队列长度限制 | 进入死信,转移到慢队列 |
| 重试次数耗尽 | 消费者拒绝 | 进入死信,人工介入 |
7. 常见问题
Q1:没有配置死信交换机,死信会怎样?
A:消息被直接丢弃。生产环境务必为重要队列配置DLX。
Q2:死信队列的消息还能再次进入死信吗?
A:会。如果死信队列也配置了DLX,消息再次变成死信会进入下一个死信队列,可能形成循环,需避免。
Q3:如何避免死信队列无限增长?
A:设置死信队列的TTL和最大长度,或定期清理。
📌 一句话总结
消息进入死信交换机有三种情况:消费者拒绝且不重新入队、TTL过期、队列满溢出。死信是RabbitMQ的"异常处理机制",而非错误。

|
🌺The End🌺点点关注,收藏不迷路🌺
|