RabbitMQ 生产端可靠投递(confirm、return、重试)
这篇解决生产端“消息到底到没到”的问题如何做到可确认、可补偿、可追踪。先说结论可靠投递要做到三件事开启publisher confirm确认消息有没有到 Broker开启return确认路由失败的消息能回来发送失败要重试但必须有上限 退避一、两种“失败”要分清发送失败消息没到 Broker比如网络抖动路由失败消息到了交换机但找不到队列这两个失败场景的处理方式完全不同。二、confirm 机制消息是否到 Broker1) 开启确认机制spring:rabbitmq:publisher-confirm-type:correlated2) 发送时带 correlationIdCorrelationDatacorrelationDatanewCorrelationData(UUID.randomUUID().toString());rabbitTemplate.convertAndSend(exchange,routingKey,message,correlationData);3) 监听确认回调rabbitTemplate.setConfirmCallback((correlationData,ack,cause)-{if(ack){// 记录成功}else{// 发送失败进入重试或落库}});三、return 机制消息是否路由到队列1) 开启 returnspring:rabbitmq:publisher-returns:truetemplate:mandatory:true2) 监听 returnrabbitTemplate.setReturnsCallback(returned-{// returned.getMessage()// returned.getReplyText()// returned.getExchange()// returned.getRoutingKey()// 路由失败需人工处理或补偿});如果 confirm 成功但 return 触发说明消息到了交换机却没队列接收。四、重试策略别无限重发推荐最简单可用的策略1s5s30s2min10min超过 5 次进入失败队列伪代码if(retryCount5){// 记录失败告警}else{// 延迟重试发送}五、一个完整可用的发送器ComponentpublicclassReliableProducer{ResourceprivateRabbitTemplaterabbitTemplate;ResourceprivateMessageSendLogServicesendLogService;publicvoidsend(Stringexchange,StringroutingKey,Objectpayload){StringmsgIdUUID.randomUUID().toString();CorrelationDatacdnewCorrelationData(msgId);// 先落库状态发送中sendLogService.saveSending(msgId,payload);rabbitTemplate.convertAndSend(exchange,routingKey,payload,cd);}PostConstructpublicvoidinitCallbacks(){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 保证到 Brokerreturn 保证到 Queue失败可重试、可补偿、可追踪做到这些消息才真的“可靠”。