RocketMQ常见问题梳理

张开发
2026/4/3 13:04:41 15 分钟阅读
RocketMQ常见问题梳理
一、MQ如何保证消息不丢失1 生产阶段消息丢失的场景单向发送同步发送后不检查SendResult失败也当成功。同步发送后不检查SendResult失败也当成功。解决措施这个阶段的目标是确保消息从客户端成功发送到 Broker。同步发送producer.send(msg)阻塞等待结果明确消息发送成功/失败配置合理重试retryTimesWhenSendFailed3网络抖动时自动重试事务消息半消息本地事务确认保证生产端与业务操作原子性2 存储阶段消息丢失的场景异步刷盘消息只写内存PageCache断电瞬间消失。异步复制主节点返回成功时从节点还没收到主节点一宕机消息就丢失。单主部署没有副本磁盘坏了消息全没。解决措施这个阶段是保证消息不丢失的核心目标是确保消息被安全持久化不会因为 Broker 宕机或重启而丢失。刷盘机制同步刷盘SYNC_FLUSHBroker 收到消息后必须将数据同步写入物理磁盘成功才会向生产者返回成功确认。这能防止消息仅停留在内存中时因 Broker 断电或重启而丢失。主从复制同步复制SYNC_MASTER在 Master-Slave 架构下Master 节点必须等待消息成功同步到至少一个 Slave 节点后才会返回成功确认。这能防止因 Master 节点宕机且无法恢复导致的消息丢失。多副本部署至少Master-Slave架构推荐DLedgerRaft多数派确认性能权衡开启同步刷盘和同步复制会显著降低系统吞吐量。对于可靠性要求极高的核心业务这是必要的权衡对于允许少量丢失的非核心业务可采用异步模式以换取更高性能。3 消费阶段消息丢失的场景自动确认消息刚拉下来就自动确认业务还没处理完。批量部分失败拉10条只成功5条却告诉Broker全消费完了。位点提交错误手动提交offset时提交的位置超过了实际处理进度。解决措施这个阶段的目标是确保消息被业务方成功处理防止因处理失败而逻辑丢失。核心机制手动确认ACK业务处理成功后显式返回CONSUME_SUCCESS失败返回RECONSUME_LATER逐条确认批量消费时逐条处理每条单独返回状态兜底方案重试队列与死信队列DLQ重试队列对于消费失败的消息Broker 会将其放入重试队列并按照指数退避或固定延迟策略如 1s, 5s, 10s...进行最多 16 次重试。死信队列DLQ当消息达到最大重试次数后仍消费失败消息将被移入死信队列。业务系统需要单独监控和消费死信队列进行人工介入或数据补偿这是防止消息丢失的最后一道防线。阶段最危险的场景最有效的解决方案生产单向发送同步发送 校验结果存储异步刷盘同步刷盘 同步复制消费自动确认手动ACK 失败重试4 MQ服务全挂了如何保证消息不丢失NameServer全挂时客户端靠本地缓存能撑一会儿Broker全挂时则完全无法收发必须靠降级落盘事后补偿兜底同时通过集群化跨AZ部署尽可能避免“全挂”的发生。降级落盘生产者的同步发送会立刻失败放弃发送到 MQ转而将消息保存到业务应用本地可靠的存储中磁盘、数据库、缓存防止数据丢失。事后补偿当 MQ 服务恢复正常后之前降级落盘的消息并不会自动发送需要一个独立的补偿程序来处理积压的数据。二、MQ如何保证消息幂等性在分布式系统中由于网络超时、重试机制或客户端重启等原因消息可能会被重复发送或重复消费。RocketMQ 本身不保证幂等它只保证消息至少投递一次At Least Once。幂等性需要由消费者业务代码自行实现。1 为什么需要幂等消息重复的典型场景阶段重复原因生产端同步发送超时客户端重试但Broker实际已收到消费端消费者处理成功但提交offset超时Broker未收到确认重新投递Broker端Master宕机触发自动切换新选举的Leader可能重复投递不处理幂等的后果订单系统同一订单多次创建 → 重复下单账户系统同一笔转账多次执行 → 余额错误库存系统同一商品多次扣减 → 超卖2 生产端幂等防止消息重复写入业务唯一ID去重每条消息携带业务唯一ID如订单号、流水号放入消息的 key消费端基于该ID去重核心在消费端。3 消费端幂等防止重复消费核心这是最关键的环节因为消费端重复投递是RocketMQ默认行为At Least Once。核心原则消费幂等 根据业务唯一标识判断是否已处理业务唯一标识最推荐消费时提取业务唯一ID如订单号、交易流水号防止重复消费。Redis原子操作使用SETNX key value EX ttl设置业务ID返回1表示首次消费 → 执行业务逻辑返回0表示已消费过 → 直接ACK状态机法业务状态驱动利用业务对象自身的状态流转天然幂等。各方案对比方案优点缺点推荐场景数据库唯一索引绝对可靠事务可保证原子性性能相对较低增加DB压力金融、订单等强一致性场景Redis SETNX性能高毫秒级可能丢失Redis无持久化需设置合理TTL高并发、可容忍极小概率重复场景业务状态机无额外存储性能最好仅适用于有状态流转的业务订单、审批流等有明确状态变更的场景三、MQ如何保证消息的顺序性RocketMQ 本身不保证全局消息顺序而是通过分区顺序机制来实现局部有序。核心思路是将需要保持顺序的消息路由到同一个队列中再由消费者串行处理该队列。1 为什么MQ需要顺序消息场景为什么需要顺序订单创建同一个订单的创建→支付→退款→物流必须按顺序处理否则状态紊乱撮合交易证券/股票交易相同出价按先出价先成交原则数据同步MySQL binlog同步下游必须按顺序还原操作2 RocketMQ顺序消息的类型类型原理适用场景全局顺序Topic只有一个队列所有消息FIFO极低吞吐场景如数据库binlog严格有序分区顺序相同消息组的消息进入同一队列不同组可并行绝大多数业务场景如按订单ID分区3 如何保证顺序性3.1 生产顺序性生产者通过某种规则如业务ID将一组有序消息发送到同一个队列中。关键点同一个业务ID的消息永远进入同一个队列队列内消息严格FIFO先进先出不同业务ID的消息可以进入不同队列并行处理3.2 消费顺序性消费者使通过顺序消费模式从同一个消息队列中串行拉取和处理消息。重试限制顺序消息的重试次数是有限的默认16次超过后:失败16次 → 跳过该消息 → 继续消费后续消息跳过的消息不会进入死信队列会永久丢失并发消费 vs 顺序消费对比项并发消费顺序消费监听器MessageListenerConcurrentlyMessageListenerOrderly拉取粒度一次拉取多批并行处理一次拉取一批串行处理队列锁无有队列级别锁吞吐量高低约并发消费的30%~50%适用场景无顺序要求的普通消息有顺序要求的业务消息四、MQ如何快速处理积压的消息80%的积压问题在消费端要提升消费速度。核心限制同一个MessageQueue最多只能被一个消费者消费因此消费者实例数 队列数。方式原理有效条件效果限制增加消费者实例更多实例分摊队列实例数 ≤ 队列数线性提升队列数不够时无效增加消费线程单实例内并行处理单实例处理能力有余量提升单机吞吐受CPU/IO限制增加队列数扩大可分配队列池需新建Topic或动态扩容突破实例数上限4.x需重建Topic5.x支持动态消费者实例数 队列数 → 多余实例空闲不分配任何队列顺序消息场景增加队列会破坏原有取模路由消息可能进错队列 →顺序被打破普通消息场景可安全操作Rebalance 会自动调整处理积压的最优路径先加实例队列有余量→ 再加线程单机有余量→ 最后加队列突破上限。

更多文章