Spring Boot整合RabbitMQ死信队列详解
为啥那么讲解死信队列,因为好多人不会使用,不知道什么场景下使用,此案例是我在公司实现的一种方式,让大家都可以学习到
一、死信队列的好处
1. 提高系统可靠性
- 避免消息丢失,确保处理失败的消息有备份
- 防止因消息处理异常导致的消息无限重试
2. 异常消息管理
- 将异常消息与正常消息分离
- 便于监控和排查问题消息
3. 灵活的重试机制
- 支持延迟重试
- 可设置不同的重试策略
4. 系统解耦
- 业务逻辑与异常处理逻辑分离
- 提高代码的可维护性
二、注解式配置说明
1. 主配置注解
typescript
@Configuration
public class RabbitMQConfig {
// 主队列
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.deadLetterExchange("dlx.exchange") // 死信交换器
.deadLetterRoutingKey("dlx.routing.key") // 死信路由键
.ttl(10000) // 消息10秒未消费进入死信
.maxLength(1000) // 队列最大长度
.build();
}
// 死信队列
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("dl.queue")
.build();
}
// 死信交换器
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange("dlx.exchange");
}
// 绑定死信交换器和队列
@Bean
public Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue())
.to(deadLetterExchange())
.with("dlx.routing.key");
}
}
2. 监听器注解
typescript
@Component
public class OrderMessageListener {
// 监听正常队列
@RabbitListener(queues = "order.queue")
public void processOrderMessage(OrderDTO order,
Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) {
try {
// 业务处理逻辑
if (processOrder(order)) {
// 手动确认
channel.basicAck(tag, false);
} else {
// 拒绝消息,进入死信队列
channel.basicNack(tag, false, false);
}
} catch (Exception e) {
// 异常时拒绝
channel.basicNack(tag, false, false);
}
}
// 监听死信队列
@RabbitListener(queues = "dl.queue")
public void processDeadLetter(OrderDTO order) {
log.error("收到死信消息: {}", order);
// 死信消息处理逻辑
handleDeadLetter(order);
}
}
三、详细整合步骤
1. 添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. 配置属性
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 开启消息返回机制
publisher-returns: true
# 开启确认机制
publisher-confirm-type: correlated
listener:
simple:
# 手动确认
acknowledge-mode: manual
# 重试配置
retry:
enabled: true
max-attempts: 3
initial-interval: 1000
3. 完整配置类
typescript
@Configuration
@Slf4j
public class RabbitMQFullConfig {
// ========== 正常业务队列配置 ==========
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.exchange", true, false);
}
@Bean
public Queue orderQueue() {
Map<String, Object> args = new HashMap<>();
// 死信交换器
args.put("x-dead-letter-exchange", "order.dlx.exchange");
// 死信路由键
args.put("x-dead-letter-routing-key", "order.dlx.key");
// 消息TTL(毫秒)
args.put("x-message-ttl", 30000);
// 队列最大长度
args.put("x-max-length", 10000);
return QueueBuilder.durable("order.queue")
.withArguments(args)
.build();
}
@Bean
public Binding orderBinding() {
return BindingBuilder.bind(orderQueue())
.to(orderExchange())
.with("order.key");
}
// ========== 死信队列配置 ==========
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange("order.dlx.exchange", true, false);
}
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("order.dl.queue")
.build();
}
@Bean
public Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue())
.to(deadLetterExchange())
.with("order.dlx.key");
}
// ========== 重试队列(延时队列替代方案)==========
@Bean
public CustomExchange delayExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange("delay.exchange",
"x-delayed-message", true, false, args);
}
@Bean
public Queue delayQueue() {
return QueueBuilder.durable("delay.queue")
.build();
}
@Bean
public Binding delayBinding() {
return BindingBuilder.bind(delayQueue())
.to(delayExchange())
.with("delay.key")
.noargs();
}
}
4. 消息生产者
java
@Component
@Slf4j
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
// 发送普通消息
public void sendOrderMessage(OrderDTO order) {
CorrelationData correlationData = new CorrelationData(order.getId());
rabbitTemplate.convertAndSend(
"order.exchange",
"order.key",
order,
message -> {
// 设置消息属性
message.getMessageProperties()
.setExpiration("30000") // 消息TTL
.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
},
correlationData
);
// 确认回调
correlationData.getFuture().addCallback(
result -> {
if (result.isAck()) {
log.info("消息发送成功: {}", order.getId());
}
},
ex -> log.error("消息发送失败: {}", ex.getMessage())
);
}
// 发送延迟消息
public void sendDelayMessage(OrderDTO order, int delayTime) {
rabbitTemplate.convertAndSend(
"delay.exchange",
"delay.key",
order,
message -> {
message.getMessageProperties()
.setHeader("x-delay", delayTime);
return message;
}
);
}
}
5. 消息消费者(完整版)
less
@Component
@Slf4j
public class OrderMessageConsumer {
private static final int MAX_RETRY_COUNT = 3;
@Autowired
private MessageProducer messageProducer;
/**
* 监听订单队列
*/
@RabbitListener(queues = "order.queue")
public void handleOrderMessage(
@Payload OrderDTO order,
@Headers Map<String, Object> headers,
Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
log.info("收到订单消息: {}", order);
// 模拟业务处理
boolean success = processOrderBusiness(order);
if (success) {
// 业务成功,确认消息
channel.basicAck(deliveryTag, false);
log.info("订单处理成功: {}", order.getId());
} else {
// 获取重试次数
Integer retryCount = (Integer) headers.get("x-retry-count");
retryCount = (retryCount == null) ? 1 : retryCount + 1;
if (retryCount <= MAX_RETRY_COUNT) {
// 重试次数未超限,重新入队
log.warn("订单处理失败,第{}次重试: {}", retryCount, order.getId());
// 设置重试计数
headers.put("x-retry-count", retryCount);
// 延迟重试
messageProducer.sendDelayMessage(order, 5000);
// 确认消息,避免重新投递
channel.basicAck(deliveryTag, false);
} else {
// 超过重试次数,进入死信队列
log.error("订单处理失败次数超过上限,进入死信队列: {}", order.getId());
channel.basicNack(deliveryTag, false, false);
}
}
} catch (Exception e) {
log.error("处理订单消息异常: {}", e.getMessage());
try {
// 拒绝消息,进入死信队列
channel.basicNack(deliveryTag, false, false);
} catch (IOException ex) {
log.error("拒绝消息失败: {}", ex.getMessage());
}
}
}
/**
* 监听死信队列
*/
@RabbitListener(queues = "order.dl.queue")
public void handleDeadLetterMessage(
@Payload OrderDTO order,
@Headers Map<String, Object> headers) {
log.error("收到死信消息: {}", order);
// 记录死信消息
logDeadLetter(order, headers);
// 发送告警
sendAlert(order);
// 人工处理或其他补偿措施
manualProcess(order);
}
/**
* 监听延迟队列
*/
@RabbitListener(queues = "delay.queue")
public void handleDelayMessage(@Payload OrderDTO order) {
log.info("收到延迟消息,开始重试: {}", order);
// 重新发送到订单队列
messageProducer.sendOrderMessage(order);
}
private boolean processOrderBusiness(OrderDTO order) {
// 业务处理逻辑
// 返回true表示成功,false表示失败
return new Random().nextBoolean();
}
private void logDeadLetter(OrderDTO order, Map<String, Object> headers) {
// 记录死信日志
log.info("记录死信: {}, headers: {}", order, headers);
}
private void sendAlert(OrderDTO order) {
// 发送告警通知
log.warn("发送告警: 订单{}处理失败", order.getId());
}
private void manualProcess(OrderDTO order) {
// 人工处理逻辑
log.info("等待人工处理订单: {}", order.getId());
}
}
四、使用场景
1. 订单超时取消
typescript
// 订单创建时发送延迟消息
public void createOrder(OrderDTO order) {
// 保存订单
orderService.save(order);
// 发送30分钟过期的消息
rabbitTemplate.convertAndSend(
"order.exchange",
"order.key",
order,
message -> {
message.getMessageProperties()
.setExpiration("1800000"); // 30分钟
return message;
}
);
}
2. 支付回调重试
typescript
// 支付回调失败时进入死信队列,人工处理
@RabbitListener(queues = "payment.callback.queue")
public void handlePaymentCallback(PaymentDTO payment) {
if (!paymentService.processCallback(payment)) {
throw new RuntimeException("支付回调处理失败");
}
}
3. 库存锁定与释放
typescript
// 库存锁定15分钟后自动释放
public void lockInventory(String orderId) {
inventoryService.lock(orderId);
// 发送15分钟后到期的消息
rabbitTemplate.convertAndSend(
"inventory.exchange",
"inventory.lock.key",
orderId,
message -> {
message.getMessageProperties()
.setExpiration("900000"); // 15分钟
return message;
}
);
}
4. 消息重试机制
kotlin
// 分级重试策略
public class RetryStrategy {
// 第一次重试:5秒后
// 第二次重试:30秒后
// 第三次重试:5分钟后
// 超过3次进入死信队列
}
五、优点总结
- 可靠性:确保消息不丢失,即使处理失败也有备份
- 灵活性:支持多种死信策略(超时、长度限制、拒绝等)
- 可维护性:异常处理与正常业务逻辑分离
- 监控性:死信队列便于监控和统计异常消息
- 可扩展性:支持多种重试和补偿机制
六、最佳实践建议
- 合理设置TTL:根据业务需求设置合适的过期时间
- 监控死信队列:设置告警,及时处理死信消息
- 限制队列大小:防止消息积压
- 记录详细日志:便于问题排查
- 死信消息分析:定期分析死信原因,优化系统
通过Spring Boot整合RabbitMQ死信队列,可以构建更加健壮、可靠的消息驱动系统,有效处理各种异常场景,提高系统的整体稳定性。