如何保证RabbitMQ消息不丢失
用 RabbitMQ 的时候最怕的就是消息丢了。一条消息从发出来到被消费掉中间要经过好几个环节每个环节都可能出问题。今天咱们就掰开了揉碎了聊聊怎么让消息一条都不丢。消息到底在哪些环节会丢一条消息的旅程大概是这样的生产者 → RabbitMQ 服务器 → 消费者。第一站生产者到 RabbitMQ 的路上。你把消息发出去结果网络抖了一下或者 RabbitMQ 正好重启了消息根本没到达服务器。你觉得发出去了其实人家根本没收到。第二站RabbitMQ 服务器内部。消息虽然到了但 RabbitMQ 只是放在内存里还没来得及写入磁盘服务器突然宕机了——重启之后内存里的东西全没了。第三站消费者拿到消息之后。消费者把消息取走了RabbitMQ 就认为这条消息已经处理完了从队列里删掉。结果消费者还没来得及处理业务逻辑就崩溃了——消息就这么丢了。下面咱们针对这三个环节逐个击破。一、生产者端确保消息真的发出去了问题消息发出去了但 RabbitMQ 没收到生产者也不知道。解决方案发布确认Publisher Confirms把信道设置成 confirm 模式RabbitMQ 收到消息后会给你回一个 ACK。收到 ACK 就说明消息安全到达了如果收到 NACK 或者超时没收到回复就知道消息丢了可以重发。// 开启 confirm 模式channel.confirmSelect();// 发送消息然后等待确认if(channel.waitForConfirms()){// 收到 ACK消息发送成功}else{// 收到 NACK需要重发}注意RabbitMQ 还有事务机制也能保证消息不丢但事务是同步阻塞的性能差生产环境基本不用。Confirm 模式是异步的性能好是推荐方案。还有一个坑消息到了交换机但没路由到任何队列这种情况 RabbitMQ 默认也不会告诉你。需要开启 mandatorytrue 配合 ReturnCallback 来捕获这种“迷路”的消息。二、Broker 端消息存到磁盘才放心问题消息到了 RabbitMQ但只存在内存里服务器一重启就没了。解决方案持久化三件套缺一不可不是说开了持久化就万事大吉了交换机、队列、消息三个都要持久化① 交换机持久化声明时设置 durabletrue② 队列持久化声明时设置 durabletrue③ 消息持久化发送时设置 deliveryMode2// 声明持久化队列channel.queueDeclare(my_queue,true,false,false,null);// 发送持久化消息channel.basicPublish(,my_queue,newAMQP.BasicProperties.Builder().deliveryMode(2)// 2 表示持久化.build(),message.getBytes());注意队列持久化 ≠ 消息自动持久化。队列持久化只是说队列的元数据名字、绑定关系等会存下来里面的消息该丢还是丢。三个都要配持久化 发布确认配合使用效果更好开启持久化后RabbitMQ 只有把消息真正写入磁盘才会给你回 ACK。这样就能确保“消息已经存到硬盘了”。三、消费者端处理完了再确认问题消费者把消息取走了RabbitMQ 就把消息删了结果消费者还没来得及处理就挂了。解决方案手动确认Manual ACK默认情况下消费者一拿到消息 RabbitMQ 就自动删除了autoAcktrue这很危险。正确的做法是关闭自动确认改用手动确认// 关闭自动确认channel.basicConsume(my_queue,false,callback);消费者处理完业务逻辑之后主动调用 basicAck 告诉 RabbitMQ“这条消息我处理完了你可以删了”。如果处理过程中出了异常就不要确认甚至可以用 basicNack 告诉 RabbitMQ 把消息重新放回队列。try{// 处理业务逻辑channel.basicAck(deliveryTag,false);// 处理成功确认}catch(Exceptione){channel.basicNack(deliveryTag,false,true);// 处理失败重新入队}进阶保障还有三道防线上面三招是基础如果对消息可靠性要求特别高还可以加几道保险镜像队列 / 仲裁队列单机 RabbitMQ 挂了消息还是可能丢。用镜像队列把数据复制到多个节点上一台机器挂了还有备份。新项目优先考虑仲裁队列Quorum Queue可靠性更高。惰性队列Lazy Queue默认情况下消息先放内存再慢慢刷盘堆积多了内存扛不住。惰性队列直接把消息写磁盘稳是稳了但性能会慢一点。死信队列DLQ消息反复消费失败怎么办别无限重试把队列堵死了把失败的消息扔到死信队列里回头再慢慢排查。总结保证消息不丢失核心就三句话环节 怎么做 一句话总结生产者 开启发布确认Publisher Confirms 没收到 ACK 就重发Broker 交换机、队列、消息三处都开启持久化 重启了还在硬盘上消费者 关闭自动确认用手动 ACK 处理完了再告诉 MQ这三个环节环环相扣任何一个环节没做好消息都有可能丢。最后提醒一句持久化和确认机制都是有性能开销的。重要的业务消息该开就开不那么重要的日志、监控类消息可以适当牺牲可靠性换性能。根据业务场景来权衡别盲目全部开到最重。