新闻详情

新闻详情

首页 / 资讯中心 / 详情

面试官:“如何保证RabbitMQ消息不丢失?”全链路排查+实战代码,一篇讲透

发布时间:2026/11/13 7:23:22来源:尧图网络
面试官:“如何保证RabbitMQ消息不丢失?”全链路排查+实战代码,一篇讲透
很多面试Java后端岗位的人都会遇到这道经典面试题今天我也来讲讲我对这道题的理解。其实面试官并不单单想听你背八股文说出“持久化”、“手动ACK”这些关键词我在面试中直接被问你实际项目中是怎么做的配置怎么配代码怎么写的等这些实际问题。面试官是想知道你是否真正理解消息从生产到消费的完整链路你是否在实际项目中踩过坑、解决过问题你是否能给出系统性的解决方案而不是零散的知识点所以回答这个问题的关键不是简单的堆砌概念而是要展示你的全链路思维。一条消息从生产者发出到被消费者消费需要经历三段路生产者 → 交换机 → 队列 → 消费者每一段都有可能丢失消息对应三个核心问题生产者 → 交换机消息发出去了但是交换机不存在或者路由规则配置错误消息直接丢了而生产者这边却不知道。交换机 → 队列 →Broker存储消息到了Broker但是Broker宕机重启内存里的消息全没了。队列 → 消费者消费者拉取到了消息还没处理完就崩了但默认自动ACK消息已经被删除了。第一关生产者端——让每条消息都有回执如果面试官追问“生产者怎么知道消息有没有发送成功?这时候你要抛出两个核心机制Publisher Confirm和Return Callback。Publisher Confirm消息到达交换机后Broker会给生产者一个确认acktrue或拒绝ackfalse。Return Callback消息到了交换机但是没有匹配到任何队列时Broker会把消息退回给生产者。两者配合就能确保生产者端不会丢失消息。在代码层面先进行配置spring: rabbitmq: publisher-confirm-type: correlated # 开启Confirm4 publisher-returns: true # 开启Return然后配置回调ConfigurationSlf4jpublicclassRabbitConfig{AutowiredprivateRabbitTemplaterabbitTemplate;PostConstructpublicvoidinit(){rabbitTemplate.setConfirmCallback((correlationData,ack,cause)-{if(ack){log.info(消息已经到达交换机,id{},correlationData.getId());//更新本地消息表状态已送达}else{log.info(消息未到达交换机,cause{},cause);//触发重试或告警}});rabbitTemplate.setReturnsCallback(returnedMessage-{log.error(消息未路由到队列, exchange{}, routingKey{},returnedMessage.getExchange(),returnedMessage.getRoutingKey());// 记录到数据库后续人工处理});}}发送消息时别忘了设置 mandatorytrue否则Return回调不会触发rabbitTemplate.setMandatory(true);rabbitTemplate.convertAndSend(order-exchange,order.create,messageBody,newCorrelationData(UUID.randomUUID().toString()));第二关Broker端——三重持久化一个都不能少面试官追问“如果Broker挂了怎么办”这时候可以告诉面试官Broker要做到消息不丢失需要三样东西同时持久化要素配置交换机durable true队列durable true消息deliveryMode 2BeanpublicQueueorderQueue(){returnQueueBuilder.durable(order-queue).withArgument(x-dead-letter-exchange,dlx-exchange).withArgument(x-dead-letter-routing-key,dlx-routing-key).build();}第三关消费者端——手动ACK 死信队列面试官追问“如果消费者处理到一半突然挂了怎么办”这里是最容易丢失消息的环节也是开发最容易踩坑的地方。默认情况下消费者是自动ACK的消息一拉取到还没处理完Broker就认为已消费并删除消息如果此时消费者突然挂了那么消息就会永久丢失。解决方案关闭自动ACK手动确认pring:rabbitmq:listener:simple:acknowledge-mode:manual# 关闭自动ACK6prefetch:1# 每次只拉一条防止消息堆积消费者代码RabbitListener(queuesorder-queue)publicvoidhandleOrder(Messagemessage,Channelchannel)throwsIOException{longdeliveryTagmessage.getMessageProperties().getDeliveryTag();try{StringbodynewString(message.getBody(),StandardCharsets.UTF_8);OrderorderJSON.parseObject(body,Order.class);// 幂等校验防止重复消费if(isAlreadyProcessed(order.getOrderId())){channel.basicAck(deliveryTag,false);return;}// 执行业务逻辑stockService.deductStock(order.getGoodsId(),order.getQuantity());// 业务成功手动ACKchannel.basicAck(deliveryTag,false);}catch(Exceptione){log.error(消费失败,e);// 重试次数控制IntegerretryCountmessage.getMessageProperties().getHeader(x-retry-count);intcurrent(retryCountnull)?0:retryCount;if(current3){// 未超限重新入队message.getMessageProperties().setHeader(x-retry-count,current1);channel.basicNack(deliveryTag,false,true);}else{// 超限拒绝消息进入死信队列log.error(重试耗尽转入死信队列);channel.basicNack(deliveryTag,false,false);}}}终极兜底本地消息表面试官如果继续追问“如果Confirm回调本身也丢了呢”这时候我们要说出终极方案——本地消息表核心思路很简单业务数据和消息记录在同一个本地事务中写入数据库。定时任务轮询“待发送”状态的消息调用MQ发送。发送成功后更新为“已发送。如果失败则进入重试超过重试次数标记为“失败”人工介入。Transactional(rollbackForException.class)publicvoidcreateOrderReliably(Orderorder){// 1. 业务入库orderMapper.insert(order);// 2. 消息记录入库同事务MessageLoglognewMessageLog();log.setMsgId(UUID.randomUUID().toString());log.setMsgBody(JSON.toJSONString(order));log.setStatus(PENDING);messageLogMapper.insert(log);}Scheduled(fixedDelay30000)publicvoidretryPendingMessages(){ListMessageLogpendingmessageLogMapper.selectByStatus(PENDING);for(MessageLogmsg:pending){if(msg.getRetryCount()5){messageLogMapper.updateStatus(msg.getMsgId(),FAILED);continue;} rabbitTemplate.convertAndSend(order-exchange,order.create,msg.getMsgBody(),newCorrelationData(msg.getMsgId()));messageLogMapper.incrementRetryCount(msg.getMsgId());}}本地消息表的作用在于把“发消息”这个动作从同步调用变成了“异步补偿”即使MQ短暂不可用消息也不会丢失。综上这个面试题可以作如下回答” 消息从生产到消费有三个环节可能丢失我的方案是全链路闭环生产者端 我开启了Publisher Confirm和Return Callback确保消息到达交换机未路由的消息也能被回收处理。对于核心业务还会配合本地消息表做最终兜底。Broker端 我确保交换机、队列、消息三者都做了持久化生产环境使用集群模式避免单点故障。消费者端 我关闭了自动ACK改为手动确认业务处理成功后才ACK。处理失败的消息会有限次重试重试耗尽后转入死信队列避免消息丢失和毒消息循环。同时业务层做好幂等处理防止重复消费。这套方案在我们项目中已经跑了很久核心业务消息零丢失。“【Java笔记 小李版】我们一起进步
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/13 0:43:49

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/12 23:59:09

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/13 10:09:00

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/13 13:31:48

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/13 16:11:45

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/13 0:02:27

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉