这篇解决生产端"消息到底到没到"的问题:如何做到可确认、可补偿、可追踪。
先说结论
可靠投递要做到三件事:
- 开启
publisher confirm,确认消息有没有到 Broker - 开启
return,确认路由失败的消息能回来 - 发送失败要重试,但必须有上限 + 退避
一、两种"失败"要分清
- 发送失败:消息没到 Broker(比如网络抖动)
- 路由失败:消息到了交换机,但找不到队列
这两个失败场景的处理方式完全不同。
二、confirm 机制(消息是否到 Broker)
1) 开启确认机制
yaml
spring:
rabbitmq:
publisher-confirm-type: correlated
2) 发送时带 correlationId
java
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
3) 监听确认回调
java
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
// 记录成功
} else {
// 发送失败,进入重试或落库
}
});
三、return 机制(消息是否路由到队列)
1) 开启 return
yaml
spring:
rabbitmq:
publisher-returns: true
template:
mandatory: true
2) 监听 return
java
rabbitTemplate.setReturnsCallback(returned -> {
// returned.getMessage()
// returned.getReplyText()
// returned.getExchange()
// returned.getRoutingKey()
// 路由失败,需人工处理或补偿
});
如果 confirm 成功但 return 触发,说明消息到了交换机却没队列接收。
四、重试策略(别无限重发)
推荐最简单可用的策略:
- 1s
- 5s
- 30s
- 2min
- 10min
- 超过 5 次进入失败队列
伪代码:
java
if (retryCount > 5) {
// 记录失败,告警
} else {
// 延迟重试发送
}
五、一个完整可用的发送器
java
@Component
public class ReliableProducer {
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private MessageSendLogService sendLogService;
public void send(String exchange, String routingKey, Object payload) {
String msgId = UUID.randomUUID().toString();
CorrelationData cd = new CorrelationData(msgId);
// 先落库(状态=发送中)
sendLogService.saveSending(msgId, payload);
rabbitTemplate.convertAndSend(exchange, routingKey, payload, cd);
}
@PostConstruct
public void initCallbacks() {
rabbitTemplate.setConfirmCallback((cd, ack, cause) -> {
if (ack) {
sendLogService.markSuccess(cd.getId());
} else {
sendLogService.markFail(cd.getId(), cause);
}
});
rabbitTemplate.setReturnsCallback(ret -> {
sendLogService.markRouteFail(
ret.getMessage().getMessageProperties().getMessageId(),
ret.getReplyText()
);
});
}
}
六、推荐的工程化落地
- 消息落库 :发送前写一条
message_log - 确认回调更新状态
- 失败消息进入补偿任务
- 监控指标:发送失败率、路由失败率、补偿成功率
这样你才知道"有没有丢",也才有"补救路径"。
最后总结
生产端可靠投递不是"一个开关",而是一套闭环:
- confirm 保证到 Broker
- return 保证到 Queue
- 失败可重试、可补偿、可追踪
做到这些,消息才真的"可靠"。