📖 开场:医院的急诊室
想象你去医院看病 🏥:
普通门诊(正常队列):
患者排队 → 医生诊治 → 治疗成功 → 出院 ✅
急诊室(死信队列):
患者病情复杂 → 普通门诊治不了 → 转到急诊室 🚑
↓
专家会诊 → 特殊处理 → 最终治愈(或转院)
这就是死信队列(Dead Letter Queue, DLQ)!
定义:无法正常处理的消息,会被转移到死信队列,进行特殊处理
🤔 什么是死信(Dead Letter)?
死信的定义
死信 = 无法被正常消费的消息 ☠️
三种情况会产生死信:
1️⃣ 消息被拒绝(Reject/Nack)
Consumer拉取消息 → 处理失败 → 拒绝消息 ❌
↓
消息变成死信 ☠️
场景:
- 消息格式错误(JSON解析失败)
- 业务逻辑无法处理
- 数据校验失败
2️⃣ 消息过期(TTL超时)
消息在队列中等待 → 超过TTL时间 → 过期 ⏰
↓
消息变成死信 ☠️
场景:
- 消息设置了TTL(生存时间)
- Consumer来不及消费
- 延迟消息场景
3️⃣ 队列满了(Queue Full)
队列已满 → 新消息进不来 → 被拒绝 🚫
↓
消息变成死信 ☠️
场景:
- 队列设置了最大长度
- 积压严重
- 内存不足
🎯 死信队列的作用
1️⃣ 消息兜底处理 🛡️
没有死信队列:
消息处理失败 → 直接丢弃 💀
↓
数据永久丢失!😱
有死信队列:
消息处理失败 → 进入死信队列 ☠️
↓
人工处理 → 重新发送 → 最终成功 ✅
2️⃣ 问题排查 🔍
作用:
- 保留失败的消息
- 分析失败原因
- 发现系统问题
示例:
死信队列积累了1000条消息 ☠️☠️☠️
↓
分析发现:都是订单金额为负数的消息
↓
发现Bug:订单金额校验有问题 🐛
↓
修复Bug → 重新处理死信消息 ✅
3️⃣ 业务降级 📉
场景:第三方服务故障
调用第三方API失败 → 消息进死信队列
↓
第三方恢复后 → 批量重新处理死信消息 ✅
4️⃣ 限流保护 🚦
场景:防止雪崩
Consumer处理过慢 → 消息积压 → 队列满
↓
新消息进死信队列(而不是压垮系统)✅
🔧 死信队列的实现
RabbitMQ的死信队列
架构设计
Producer
↓
┌─────────────────────────────────────────┐
│ 普通交换机 │
│ (Normal Exchange) │
└─────────────┬───────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ 普通队列 │
│ (Normal Queue) │
│ │
│ 设置: │
│ - x-dead-letter-exchange: dlx │
│ - x-dead-letter-routing-key: dlq.key │
│ - x-message-ttl: 60000 (可选) │
│ - x-max-length: 10000 (可选) │
└─────────────┬───────────────────────────┘
│
│ 消息变成死信:
│ 1. 被拒绝(requeue=false)
│ 2. TTL过期
│ 3. 队列满
↓
┌─────────────────────────────────────────┐
│ 死信交换机 │
│ (Dead Letter Exchange) │
└─────────────┬───────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ 死信队列 │
│ (Dead Letter Queue) │
└─────────────┬───────────────────────────┘
│
↓
Dead Letter Consumer
(专门处理死信的消费者)
配置代码
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DeadLetterQueueConfig {
// ========== 普通队列配置 ==========
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal.queue")
// ⭐ 配置死信交换机
.withArgument("x-dead-letter-exchange", "dlx.exchange")
// ⭐ 配置死信路由键
.withArgument("x-dead-letter-routing-key", "dlq.routing.key")
// ⭐ 可选:设置TTL(60秒)
.withArgument("x-message-ttl", 60000)
// ⭐ 可选:设置队列最大长度
.withArgument("x-max-length", 10000)
.build();
}
@Bean
public DirectExchange normalExchange() {
return new DirectExchange("normal.exchange");
}
@Bean
public Binding normalBinding(Queue normalQueue, DirectExchange normalExchange) {
return BindingBuilder.bind(normalQueue)
.to(normalExchange)
.with("normal.routing.key");
}
// ========== 死信队列配置 ==========
@Bean
public Queue deadLetterQueue() {
return new Queue("dead.letter.queue", true);
}
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange("dlx.exchange");
}
@Bean
public Binding deadLetterBinding(Queue deadLetterQueue, DirectExchange deadLetterExchange) {
return BindingBuilder.bind(deadLetterQueue)
.to(deadLetterExchange)
.with("dlq.routing.key");
}
}
生产者
@Service
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 发送普通消息
*/
public void sendMessage(String message) {
System.out.println("发送消息: " + message);
rabbitTemplate.convertAndSend(
"normal.exchange",
"normal.routing.key",
message
);
}
/**
* 发送带TTL的消息
*/
public void sendMessageWithTTL(String message, long ttl) {
System.out.println("发送消息(TTL=" + ttl + "ms): " + message);
rabbitTemplate.convertAndSend(
"normal.exchange",
"normal.routing.key",
message,
msg -> {
// ⭐ 设置消息TTL
msg.getMessageProperties().setExpiration(String.valueOf(ttl));
return msg;
}
);
}
}
普通消费者(会拒绝消息)
@Component
public class NormalConsumer {
/**
* 消费普通队列
*/
@RabbitListener(queues = "normal.queue")
public void consumeMessage(Message message, Channel channel) throws IOException {
String msg = new String(message.getBody());
long deliveryTag = message.getMessageProperties().getDeliveryTag();
System.out.println("收到消息: " + msg);
try {
// 业务处理
processMessage(msg);
// ⭐ 处理成功,确认
channel.basicAck(deliveryTag, false);
System.out.println("✅ 消息处理成功");
} catch (Exception e) {
System.err.println("❌ 消息处理失败: " + e.getMessage());
// ⭐ 拒绝消息,不重新入队(进入死信队列)
channel.basicNack(deliveryTag, false, false);
// ↑
// requeue=false(不重新入队)
}
}
private void processMessage(String msg) throws Exception {
// 模拟处理逻辑
if (msg.contains("error")) {
throw new Exception("消息格式错误");
}
// 正常处理...
}
}
死信消费者(处理死信)
@Component
@Slf4j
public class DeadLetterConsumer {
@Autowired
private MessageProducer producer;
/**
* 消费死信队列
*/
@RabbitListener(queues = "dead.letter.queue")
public void handleDeadLetter(Message message) {
String msg = new String(message.getBody());
// 获取死信原因
String reason = getDeadLetterReason(message);
log.warn("☠️ 收到死信消息: {}, 原因: {}", msg, reason);
// ⭐ 根据不同原因,采取不同策略
switch (reason) {
case "rejected":
// 被拒绝:记录日志,人工处理
handleRejectedMessage(msg, message);
break;
case "expired":
// 过期:可能需要重新发送
handleExpiredMessage(msg);
break;
case "maxlen":
// 队列满:等待后重试
handleQueueFullMessage(msg);
break;
default:
log.error("未知的死信原因: {}", reason);
}
}
/**
* 获取死信原因
*/
private String getDeadLetterReason(Message message) {
MessageProperties props = message.getMessageProperties();
// 从x-death头获取死信信息
List<Map<String, ?>> xDeath = props.getXDeathHeader();
if (xDeath != null && !xDeath.isEmpty()) {
Map<String, ?> death = xDeath.get(0);
return (String) death.get("reason");
}
return "unknown";
}
/**
* 处理被拒绝的消息
*/
private void handleRejectedMessage(String msg, Message message) {
log.info("处理被拒绝的消息: {}", msg);
// 策略1:记录到数据库,人工处理
saveToDatabase(msg, message);
// 策略2:发送告警
sendAlert("消息被拒绝: " + msg);
// 策略3:如果可以修复,重新发送
if (canRetry(msg)) {
String fixedMsg = fixMessage(msg);
producer.sendMessage(fixedMsg);
log.info("消息已修复并重新发送: {}", fixedMsg);
}
}
/**
* 处理过期的消息
*/
private void handleExpiredMessage(String msg) {
log.info("处理过期的消息: {}", msg);
// 策略1:判断是否还需要处理
if (isStillValid(msg)) {
// 重新发送
producer.sendMessage(msg);
log.info("消息仍然有效,重新发送: {}", msg);
} else {
log.info("消息已过期且无效,丢弃: {}", msg);
}
}
/**
* 处理队列满的消息
*/
private void handleQueueFullMessage(String msg) {
log.info("处理队列满的消息: {}", msg);
// 策略:等待一段时间后重试
try {
Thread.sleep(5000); // 等待5秒
producer.sendMessage(msg);
log.info("重新发送消息: {}", msg);
} catch (InterruptedException e) {
log.error("等待被中断", e);
}
}
// 辅助方法(示意)
private void saveToDatabase(String msg, Message message) {
// 保存到数据库
}
private void sendAlert(String alert) {
// 发送告警
}
private boolean canRetry(String msg) {
return !msg.contains("fatal");
}
private String fixMessage(String msg) {
return msg.replace("error", "fixed");
}
private boolean isStillValid(String msg) {
return true; // 判断逻辑
}
}
RocketMQ的死信队列
特点
RocketMQ会自动创建死信队列:
普通Topic: OrderTopic
↓
消息消费失败,重试16次后
↓
自动进入死信Topic: %DLQ%ConsumerGroupName
死信Topic命名规则:
%DLQ% + 消费者组名
例如:
消费者组:order-consumer-group
死信Topic:%DLQ%order-consumer-group
消费者代码
@Component
@Slf4j
public class OrderConsumer {
/**
* 消费普通消息
*/
@RocketMQMessageListener(
topic = "OrderTopic",
consumerGroup = "order-consumer-group"
)
public class OrderMessageListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("收到消息: {}", message);
try {
// 业务处理
processOrder(message);
log.info("✅ 消息处理成功");
} catch (Exception e) {
log.error("❌ 消息处理失败", e);
// ⭐ 抛异常,RocketMQ会自动重试
// 重试16次后,自动进入死信队列
throw new RuntimeException("处理失败", e);
}
}
private void processOrder(String message) throws Exception {
// 处理逻辑
if (message.contains("error")) {
throw new Exception("订单格式错误");
}
}
}
}
死信消费者
@Component
@Slf4j
public class DeadLetterConsumer {
@Autowired
private OrderService orderService;
/**
* 消费死信队列
* Topic名称:%DLQ% + 消费者组名
*/
@RocketMQMessageListener(
topic = "%DLQ%order-consumer-group",
consumerGroup = "dlq-order-consumer-group"
)
public class DLQMessageListener implements RocketMQListener<MessageExt> {
@Override
public void onMessage(MessageExt message) {
String body = new String(message.getBody());
log.warn("☠️ 收到死信消息: msgId={}, body={}",
message.getMsgId(), body);
// 获取重试次数
int reconsumeTimes = message.getReconsumeTimes();
log.info("重试次数: {}", reconsumeTimes);
// ⭐ 处理死信消息
handleDeadLetter(body, message);
}
private void handleDeadLetter(String body, MessageExt message) {
// 策略1:保存到数据库,人工处理
DeadLetterRecord record = new DeadLetterRecord();
record.setMsgId(message.getMsgId());
record.setBody(body);
record.setTopic(message.getTopic());
record.setRetryTimes(message.getReconsumeTimes());
record.setCreateTime(new Date());
deadLetterRepository.save(record);
log.info("死信消息已保存到数据库");
// 策略2:发送告警
alertService.send("死信队列有新消息: " + message.getMsgId());
// 策略3:尝试修复并重新发送
if (canFix(body)) {
String fixedBody = fix(body);
orderService.republishMessage(fixedBody);
log.info("消息已修复并重新发送");
}
}
private boolean canFix(String body) {
// 判断是否可以修复
return !body.contains("fatal");
}
private String fix(String body) {
// 修复逻辑
return body.replace("error", "fixed");
}
}
}
Kafka的"死信队列"(自己实现)
Kafka本身不支持死信队列,需要自己实现
实现方案
@Component
@Slf4j
public class KafkaConsumerWithDLQ {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
private static final String NORMAL_TOPIC = "orders";
private static final String DLQ_TOPIC = "orders-dlq"; // 死信Topic
private static final int MAX_RETRY = 3;
@KafkaListener(topics = NORMAL_TOPIC, groupId = "order-consumer-group")
public void consumeMessage(ConsumerRecord<String, String> record) {
String message = record.value();
log.info("收到消息: {}", message);
try {
// 业务处理
processMessage(message);
log.info("✅ 消息处理成功");
} catch (Exception e) {
log.error("❌ 消息处理失败", e);
// ⭐ 获取重试次数(从消息头)
int retryCount = getRetryCount(record);
if (retryCount < MAX_RETRY) {
// 还可以重试,重新发送到原Topic
retryCount++;
republishWithRetryCount(NORMAL_TOPIC, message, retryCount);
log.info("消息重新发送,第{}次重试", retryCount);
} else {
// ⭐ 重试次数用完,发送到死信Topic
sendToDeadLetterQueue(record, e);
log.warn("☠️ 消息发送到死信队列");
}
}
}
/**
* 获取重试次数
*/
private int getRetryCount(ConsumerRecord<String, String> record) {
// 从消息头获取重试次数
Header header = record.headers().lastHeader("retry-count");
if (header != null) {
return Integer.parseInt(new String(header.value()));
}
return 0;
}
/**
* 重新发送消息(带重试次数)
*/
private void republishWithRetryCount(String topic, String message, int retryCount) {
ProducerRecord<String, String> record = new ProducerRecord<>(topic, message);
// ⭐ 添加重试次数到消息头
record.headers().add("retry-count", String.valueOf(retryCount).getBytes());
kafkaTemplate.send(record);
}
/**
* 发送到死信队列
*/
private void sendToDeadLetterQueue(ConsumerRecord<String, String> record, Exception e) {
String message = record.value();
// 构造死信消息
DeadLetterMessage dlqMsg = new DeadLetterMessage();
dlqMsg.setOriginalTopic(record.topic());
dlqMsg.setOriginalPartition(record.partition());
dlqMsg.setOriginalOffset(record.offset());
dlqMsg.setMessage(message);
dlqMsg.setErrorMessage(e.getMessage());
dlqMsg.setTimestamp(System.currentTimeMillis());
// ⭐ 发送到死信Topic
kafkaTemplate.send(DLQ_TOPIC, JSON.toJSONString(dlqMsg));
}
private void processMessage(String message) throws Exception {
// 业务逻辑
if (message.contains("error")) {
throw new Exception("消息格式错误");
}
}
}
@Data
class DeadLetterMessage {
private String originalTopic;
private int originalPartition;
private long originalOffset;
private String message;
private String errorMessage;
private long timestamp;
}
死信消费者
@Component
@Slf4j
public class KafkaDLQConsumer {
@KafkaListener(topics = "orders-dlq", groupId = "dlq-consumer-group")
public void handleDeadLetter(ConsumerRecord<String, String> record) {
String dlqMsgJson = record.value();
DeadLetterMessage dlqMsg = JSON.parseObject(dlqMsgJson, DeadLetterMessage.class);
log.warn("☠️ 收到死信消息: topic={}, offset={}, error={}",
dlqMsg.getOriginalTopic(),
dlqMsg.getOriginalOffset(),
dlqMsg.getErrorMessage());
// 处理死信
handleDeadLetter(dlqMsg);
}
private void handleDeadLetter(DeadLetterMessage dlqMsg) {
// 保存到数据库
deadLetterRepository.save(dlqMsg);
// 发送告警
alertService.send("死信队列有新消息");
// 尝试修复
if (canFix(dlqMsg.getMessage())) {
String fixed = fix(dlqMsg.getMessage());
kafkaTemplate.send(dlqMsg.getOriginalTopic(), fixed);
}
}
}
🎯 死信处理策略
策略1:人工处理 👨💼
适用:消息量少,问题复杂
/**
* 死信管理后台
*/
@RestController
@RequestMapping("/api/dlq")
public class DeadLetterController {
@Autowired
private DeadLetterRepository dlqRepo;
@Autowired
private MessageProducer producer;
/**
* 查询死信列表
*/
@GetMapping("/list")
public List<DeadLetterRecord> listDeadLetters() {
return dlqRepo.findAll();
}
/**
* 重新发送死信消息
*/
@PostMapping("/{id}/retry")
public void retryDeadLetter(@PathVariable Long id) {
DeadLetterRecord record = dlqRepo.findById(id)
.orElseThrow(() -> new NotFoundException("死信不存在"));
// ⭐ 重新发送到原Topic
producer.sendMessage(record.getOriginalTopic(), record.getMessage());
// 标记为已处理
record.setStatus("RETRIED");
record.setRetryTime(new Date());
dlqRepo.save(record);
}
/**
* 删除死信消息
*/
@DeleteMapping("/{id}")
public void deleteDeadLetter(@PathVariable Long id) {
dlqRepo.deleteById(id);
}
}
策略2:自动重试(有限次数)🔄
@Component
@Slf4j
public class AutoRetryDeadLetterHandler {
private static final int MAX_AUTO_RETRY = 3;
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void autoRetryDeadLetters() {
// 查询待重试的死信
List<DeadLetterRecord> records = dlqRepo.findByStatus("PENDING");
for (DeadLetterRecord record : records) {
if (record.getAutoRetryCount() < MAX_AUTO_RETRY) {
try {
// ⭐ 自动重试
producer.sendMessage(record.getOriginalTopic(), record.getMessage());
record.setAutoRetryCount(record.getAutoRetryCount() + 1);
record.setLastRetryTime(new Date());
dlqRepo.save(record);
log.info("自动重试死信消息: id={}, 第{}次",
record.getId(), record.getAutoRetryCount());
} catch (Exception e) {
log.error("自动重试失败", e);
}
} else {
// 重试次数用完,标记为需要人工处理
record.setStatus("MANUAL_REQUIRED");
dlqRepo.save(record);
// 发送告警
alertService.send("死信消息需要人工处理: id=" + record.getId());
}
}
}
}
策略3:定期清理 🗑️
@Component
@Slf4j
public class DeadLetterCleaner {
/**
* 每天凌晨清理7天前的死信
*/
@Scheduled(cron = "0 0 2 * * ?")
public void cleanOldDeadLetters() {
Date expireTime = DateUtils.addDays(new Date(), -7);
// 查询7天前的死信
List<DeadLetterRecord> oldRecords = dlqRepo.findByCreateTimeBefore(expireTime);
if (!oldRecords.isEmpty()) {
// 备份到归档表
dlqArchiveRepo.saveAll(oldRecords);
// 删除
dlqRepo.deleteAll(oldRecords);
log.info("清理旧死信消息: count={}", oldRecords.size());
}
}
}
策略4:分类处理 🏷️
@Component
@Slf4j
public class DeadLetterClassifier {
public void handleDeadLetter(DeadLetterMessage dlqMsg) {
// ⭐ 根据错误类型分类处理
String errorType = classifyError(dlqMsg.getErrorMessage());
switch (errorType) {
case "FORMAT_ERROR":
// 格式错误:尝试修复
handleFormatError(dlqMsg);
break;
case "BUSINESS_ERROR":
// 业务错误:人工处理
handleBusinessError(dlqMsg);
break;
case "TIMEOUT":
// 超时:重试
handleTimeout(dlqMsg);
break;
case "THIRD_PARTY_ERROR":
// 第三方错误:等待第三方恢复后重试
handleThirdPartyError(dlqMsg);
break;
default:
log.error("未知错误类型: {}", errorType);
}
}
private String classifyError(String errorMessage) {
if (errorMessage.contains("JSON") || errorMessage.contains("format")) {
return "FORMAT_ERROR";
} else if (errorMessage.contains("timeout")) {
return "TIMEOUT";
} else if (errorMessage.contains("API")) {
return "THIRD_PARTY_ERROR";
} else {
return "BUSINESS_ERROR";
}
}
private void handleFormatError(DeadLetterMessage dlqMsg) {
// 尝试修复格式错误
log.info("处理格式错误: {}", dlqMsg.getMessage());
// ...
}
private void handleBusinessError(DeadLetterMessage dlqMsg) {
// 业务错误,标记为需要人工处理
log.warn("业务错误,需要人工处理: {}", dlqMsg.getMessage());
// ...
}
private void handleTimeout(DeadLetterMessage dlqMsg) {
// 超时,重新发送
log.info("超时,重新发送: {}", dlqMsg.getMessage());
// ...
}
private void handleThirdPartyError(DeadLetterMessage dlqMsg) {
// 第三方错误,延迟重试
log.info("第三方错误,5分钟后重试: {}", dlqMsg.getMessage());
// ...
}
}
📊 对比总结
| 消息队列 | 死信队列支持 | 实现方式 | 死信原因 |
|---|---|---|---|
| RabbitMQ | ✅ 原生支持 | 配置x-dead-letter-exchange | 拒绝、过期、队列满 |
| RocketMQ | ✅ 原生支持 | 自动创建%DLQ%GroupName | 重试16次后 |
| Kafka | ❌ 不支持 | 需自己实现 | 自定义逻辑 |
🎓 面试题速答
Q1: 什么是死信队列?
A: 死信队列(DLQ) = 存放无法正常处理的消息的队列
三种情况产生死信:
- 消息被拒绝(Reject/Nack)
- 消息过期(TTL超时)
- 队列满了(Queue Full)
作用:
- 消息兜底,防止数据丢失
- 问题排查,分析失败原因
- 业务降级,延迟处理
Q2: RabbitMQ如何配置死信队列?
A: 普通队列配置:
QueueBuilder.durable("normal.queue")
.withArgument("x-dead-letter-exchange", "dlx.exchange") // 死信交换机
.withArgument("x-dead-letter-routing-key", "dlq.key") // 死信路由键
.build();
消费者拒绝消息:
channel.basicNack(deliveryTag, false, false);
// ↑
// requeue=false(进入死信队列)
Q3: RocketMQ的死信队列是如何工作的?
A: 自动创建死信Topic:
普通Topic: OrderTopic
消费者组: order-consumer-group
消息消费失败,重试16次后
↓
自动进入死信Topic: %DLQ%order-consumer-group
消费死信:
@RocketMQMessageListener(
topic = "%DLQ%order-consumer-group",
consumerGroup = "dlq-consumer-group"
)
Q4: Kafka如何实现死信队列?
A: Kafka不支持死信队列,需自己实现:
try {
processMessage(message);
} catch (Exception e) {
if (retryCount < MAX_RETRY) {
// 重试
republish(message, retryCount + 1);
} else {
// ⭐ 发送到死信Topic
kafkaTemplate.send("orders-dlq", message);
}
}
Q5: 死信消息如何处理?
A: 四种策略!
- 人工处理:提供管理后台,人工审核和重发
- 自动重试:定时任务自动重试(有限次数)
- 定期清理:清理过期的死信消息
- 分类处理:根据错误类型采取不同策略
Q6: 死信队列的最佳实践?
A:
- 监控告警:死信队列有新消息,立即告警
- 保留时间:死信保留7-30天
- 定期清理:过期死信归档后删除
- 分析原因:定期分析死信,发现系统问题
- 人工介入:重要业务的死信,需要人工处理
🎬 总结
死信队列处理流程图
┌──────────────────────────────────────────────┐
│ 正常队列 │
│ │
│ 消息 → 消费 → 处理 │
│ ↓ │
│ 成功 ✅ → 确认 │
│ ↓ │
│ 失败 ❌ │
│ ├─ 重试3次 │
│ ├─ 消息过期 │
│ └─ 队列满 │
│ │
└──────────────┬───────────────────────────────┘
│
↓ 进入死信队列
┌──────────────────────────────────────────────┐
│ 死信队列 ☠️ │
│ │
│ 消息暂存 → 分析原因 → 处理策略 │
│ ↓ │
│ ┌────┴────┐ │
│ │ │ │
│ 格式错误 业务错误 │
│ ↓ ↓ │
│ 自动修复 人工处理 │
│ ↓ ↓ │
│ 重新发送 解决后重发 │
│ │
└──────────────────────────────────────────────┘
死信队列 = 消息的"急诊室" 🏥
🎉 恭喜你!
你已经完全掌握了死信队列的作用和处理策略!🎊
核心要点:
- 死信:无法正常处理的消息(拒绝、过期、队列满)
- 作用:兜底处理、问题排查、业务降级
- 策略:人工处理、自动重试、定期清理、分类处理
下次面试,这样回答:
"死信队列是用来存放无法正常处理的消息的队列。
三种情况会产生死信:消息被拒绝、消息过期、队列满了。
RabbitMQ通过配置x-dead-letter-exchange实现,RocketMQ会自动创建死信Topic(%DLQ%GroupName),Kafka需要自己实现。
死信消息的处理策略包括:人工处理(提供管理后台)、自动重试(定时任务)、定期清理(归档后删除)、分类处理(根据错误类型)。
我们项目中对死信队列配置了监控告警,有新消息立即通知,重要业务的死信需要人工审核后才能重发。"
面试官:👍 "很好!你对死信队列理解很全面!"
本文完 🎬
上一篇: 191-消息队列的推拉模式的优劣对比.md
下一篇: 193-消息队列的削峰填谷作用和实际应用.md
作者注:写完这篇,我都想去医院急诊室实习了!🏥
如果这篇文章对你有帮助,请给我一个Star⭐!