RocketMQ的可靠性设计覆盖了消息流转的每个环节但不是说用了RocketMQ消息就不会丢。它提供了机制能不能用好取决于你怎么配、怎么写代码。一条消息从发送到被消费要过三道关卡生产端把消息送到Broker发送关卡Broker把消息持久化到磁盘存储关卡消费端拉取消息并确认消费成功消费关卡任何一道关卡出问题消息就可能丢。接下来分别看看RocketMQ在每道关卡上做了什么设计。发送关生产端到Broker三种发送方式的可靠性差异RocketMQ提供三种发送方式同步发送、异步发送、单向发送。它们在可靠性上的差距很大。同步发送会阻塞当前线程等待Broker的响应。拿到响应后可以判断发送状态失败了可以重试或者做补偿。这是最可靠的发送方式。异步发送不阻塞当前线程通过回调函数拿到发送结果。可靠性取决于回调函数的实现质量。单向发送调完就走不等响应不知道成功还是失败。适合日志采集这类允许丢消息的场景。发送方式是否阻塞能否感知失败内部自动重试适用场景同步发送是能通过返回值判断有默认重试2次交易、支付等不能丢消息的场景异步发送否能通过回调判断有网络层重试但不切换Broker响应时间敏感但仍需保证可靠的场景单向发送否不能无日志采集、监控指标上报等允许丢失的场景这里有个容易踩的坑异步发送在重试时不会切换Broker。同步发送失败后会自动换一个Broker再发一次异步发送不会。异步发送虽然也有网络层重试默认2次但每次重试都是往同一个Broker发不具备故障转移能力。如果某个Broker出了问题异步发送的可靠性要比同步发送低一截。发送重试与故障规避同步发送的重试逻辑在DefaultMQProducerImpl的sendDefaultImpl方法中。总尝试次数 1 retryTimesWhenSendFailed默认值是2也就是最多尝试3次。每次重试时会传入上一次使用的BrokerName选队列时主动规避这个Broker避免反复往同一个有问题的节点上发。发送失败或抛异常时客户端还会更新内部的故障表。MQFaultStrategy维护了一套延迟分级规避机制根据某个Broker的响应耗时把它标记为一段时间内不可用。比如响应耗时超过2000ms这个Broker会被标记为120秒不可用如果直接抛了网络异常不可用时长会拉到10分钟。在这段时间内发送消息时会优先选择其他Broker。这个机制默认是关闭的需要手动开启producer.setSendLatencyFaultEnable(true);对于多Broker部署的集群建议开启这个配置。它让生产端在重试时能更有效地避开有问题的Broker提高发送成功率。存储关Broker的持久化消息到了Broker写入CommitLog之后还不算安全。消息先写到操作系统的PageCache里如果这时候机器断电PageCache里的数据就没了。需要把数据从PageCache刷到磁盘上这个过程叫刷盘。RocketMQ提供两种刷盘策略同步刷盘和异步刷盘。同步刷盘同步刷盘由CommitLog内部的GroupCommitService实现。消息写入PageCache后发送线程不会立即返回而是阻塞等待刷盘线程把数据写到磁盘。刷盘完成后才唤醒发送线程返回写入成功。GroupCommitService的设计用了读写队列交换的方式来减少锁竞争新的刷盘请求写入写队列刷盘线程从读队列取请求处理两个队列定期交换。这样写入和刷盘互不阻塞。刷盘时对每个请求最多执行两次flush。为什么是两次因为一条消息可能恰好写在一个MappedFile的尾部跨到了下一个文件第一次flush刷当前文件第二次flush刷下一个文件两次足以覆盖这种边界情况。如果两次flush后位点仍未达标返回FLUSH_DISK_TIMEOUT。同步刷盘保证了每条消息都落盘后才返回成功数据可靠性最高但对磁盘IO压力大吞吐量会受影响。异步刷盘异步刷盘由FlushRealTimeService实现。消息写入PageCache后立即返回成功后台线程定时把PageCache的数据刷到磁盘。默认每500ms刷一次且至少积累4个页16KB才触发。另外还有一个全量刷盘间隔默认10秒超过这个时间即使不够4页也会强制刷。异步刷盘的吞吐量更高但如果Broker在两次刷盘之间宕机PageCache中还没来得及落盘的消息就会丢失。主从同步刷盘解决的是单机持久化的问题但如果磁盘坏了呢这就需要主从复制。HAService负责主从之间的数据同步。Master维护一个字段记录已经同步到Slave的最大位点用来判断Slave是否健康必须有活跃连接且主从数据差距不超过256MB默认值。RocketMQ支持两种主从同步模式同步复制SYNC_MASTER消息写入Master后等待至少一个Slave确认收到才返回成功异步复制ASYNC_MASTER消息写入Master后立即返回成功后台异步推送给Slave可靠性与性能的组合选择刷盘策略和主从模式可以自由组合不同组合对应不同的可靠性和性能水平刷盘策略主从模式可靠性性能适用场景同步刷盘同步复制消息不会丢失吞吐量最低金融级交易对数据零容忍同步刷盘异步复制Master磁盘不坏就不丢吞吐量较低核心业务可接受极端情况下极少丢失异步刷盘同步复制Master宕机可能丢少量吞吐量较高重要业务对吞吐有要求异步刷盘异步复制宕机会丢PageCache中未刷数据吞吐量最高日志类、可容忍少量丢失的业务绝大多数线上系统用的是「异步刷盘 异步复制」或者「异步刷盘 同步复制」。同步刷盘对磁盘IO的压力很大在高并发场景下会成为瓶颈。实际选择时建议先看业务对消息丢失的容忍程度再决定配置。大多数互联网业务场景下异步刷盘 同步复制是一个比较均衡的选择。对应的Broker配置项flushDiskTypeASYNC_FLUSH brokerRoleSYNC_MASTER消费关Broker到消费端消息存储可靠了消费端也可能丢消息。最常见的情况是消费位点提前提交了消息还没处理完消费者重启后这条消息就不会再被拉取。RocketMQ在消费端的可靠性设计围绕一个原则先消费成功后才提交位点。消费确认机制消费者拉取消息后由ConsumeMessageConcurrentlyService负责并发消费。消费结果的处理分两种情况集群模式下消费失败的消息通过sendMessageBack发送回Broker进入重试队列等待下次投递。如果sendMessageBack本身也失败了比如网络断了会把消息加入本地的失败列表5秒后重新提交消费。广播模式下消费失败直接丢弃只打了一行warn日志。这个行为很多人不知道。如果业务用了广播模式又要求消息不能丢需要在业务层自己实现重试逻辑。消费重试的延时策略消息发送回Broker后Broker根据已重试次数决定下次投递的延时。具体逻辑是延时级别 3 已重试次数。RocketMQ内置了18个延时级别从1秒到2小时第一次重试从第3级10秒开始逐级递增。完整的重试时间表重试次数间隔时间重试次数间隔时间第1次10秒第9次7分钟第2次30秒第10次8分钟第3次1分钟第11次9分钟第4次2分钟第12次10分钟第5次3分钟第13次20分钟第6次4分钟第14次30分钟第7次5分钟第15次1小时第8次6分钟第16次2小时死信队列当重试次数达到上限默认16次由SubscriptionGroupConfig的retryMaxTimes配置消息不再重试转入死信队列。死信队列的Topic命名格式是%DLQ%加上消费者组名。进入死信队列的消息不会自动消费需要人工介入处理。线上系统必须对死信队列做监控。可以通过RocketMQ的Dashboard查看死信队列的消息数量也可以写一个专门的消费者订阅死信队列Topic做告警或者人工补偿。消费位点的提交时机消费完成后客户端从本地的ProcessQueue中移除已消费的消息返回剩余消息的最小位点然后更新消费位点。这意味着如果有一批消息同时在消费只有所有靠前的消息都处理完了位点才会往前推进。代价是如果某条靠前的消息一直消费失败后面的消息即使消费成功了位点也不会前进。消费者重启后会从旧位点重新拉取导致后面那些已经消费成功的消息被重复消费。这就是RocketMQ选择了至少一次语义的体现业务方需要做幂等来应对重复消费。生产环境落地方案把三道关卡的最佳实践汇总成一份可以直接用的方案生产端使用同步发送检查返回的SendStatus是否为SEND_OK设置retryTimesWhenSendFailed为3多重试一次开启sendLatencyFaultEnable让重试更有效设置合理的sendMsgTimeout建议5秒Broker端核心业务推荐异步刷盘 同步主从兼顾性能和可靠性金融场景用同步刷盘 同步主从绝对不丢消费端消费逻辑抛异常时返回RECONSUME_LATER不要吞掉异常返回CONSUME_SUCCESS业务代码做好幂等用唯一消息ID或业务唯一键去重根据业务需要调整最大重试次数对于处理时间较长的消息适当增加对死信队列配置监控告警定期检查是否有消息进入死信小结消息可靠性是个全链路问题不是配一个参数就能解决的。RocketMQ在每个环节都提供了保障机制但每种机制都有对应的性能代价。同步刷盘保证了磁盘级别的可靠性但会明显拉低吞吐量。同步主从保证了跨节点的冗余但每条消息的响应时间都会增加一个网络往返。我的建议是不要追求理论上的零丢失。在实际系统中把三道关卡中最薄弱的那一道补强就够了。比如你的集群已经有了主从同步那么单机的异步刷盘在大多数场景下就足够用了没必要再开同步刷盘把性能再砍一刀。对业务代码的要求反而是最容易被忽视也最容易出问题的环节。发送端不检查发送结果、消费端吞掉异常返回成功、没有死信队列监控这些代码层面的疏忽造成的消息丢失比Broker配置问题要多得多。最近在知乎出了「应付6000万会员的秒杀系统专栏」和「几亿用户,百万并发的C端商品系统实战」专栏感兴趣的可以订阅一下。至于知识星球的可以搜老码头的技术浮生录它是一个能实际帮你解决难题的星球。有问题的找知心的Sam哥支持无限次语音一对一解决你遇到的难题。「另外后续我新写的所有对外的付费专栏在星球内都是免费的且可以拿到所有源代码。」知识星球内后续将推出20个付费专栏覆盖电商全链路选购线用户会员营销线中后台购物车服务营销系统订单系统商品服务用户系统支付系统菜单服务结算服务从前台选购到中后台结算星球成员全部免费后续新增也不额外收费。我的知乎账号:SamDeepThinking