探秘 RocketMQ 事务机制,如何保证消息零丢失
真正的大师永远怀着一颗学徒的心
引言
事务的概念就不用多说了,我相信阅读文章的童鞋都是有着非常深刻的认识。我们都知道MQ
可以实现微服务之间的异步以及解耦,那么引入MQ
之后,如何实现微服务之间的数据一致性是一个值得思考的问题。RocketMQ 事务消息正是解决这个问题的解决方案。另外事务消息也是为了解决消息丢失问题。
哪些场景会出现消息丢失
在分析RocketMQ
事务消息之前,我们先来分析下引入消息中间件之后,整个消息链路在哪些场景会出现消息丢失的异常情况。
当我们支付订单之后,我们账户的购物积分也会进行相应的积分调整。我们结合下面的订单服务、RocketMQ
、积分服务的简化交互图来看,我们来分析下整个链路中可能会出现的消息丢失问题。
场景 1:
在订单服务向 RocketMQ 发送订单成功生成的消息的时候,可能由于网络抖动的问题导致订单消息没能正常投递到 RocketMQ,导致消息丢失。
场景 2:
那么假如订单服务以及 RocketMQ 之间的网络没问题,消息正常被 RocketMQ 接收到了,那么会存在消息丢失的情况吗?答案是肯定的,这和 RocketMQ 的持久化机制有关系,当消息到达 RocketMQ 之后,并不是立马落盘存储,而是存储在 page cache 中的。如果此时出现服务器断电或者宕机情况,那么还没来得及落盘的消息数据就有可能丢失。另外即使是落到磁盘当中,如果出现磁盘坏道的话,依然会出现消息数据的可能。
场景 3:
如果前面两种场景都没问题,积分服务拿到订单消息了。还会出现消息丢失的问题吗?答案依然是肯定的。即便是积分服务拿到了订单消息,当积分服务自动提交消息 offset 到 RocketMQ 中,但是此时如果出现宕机或者积分服务挂了,没有将本该增加的积分进行处理,此时也就出现了消息丢失的情况。
事务消息机制原理
half 消息
所谓的RocketMQ
事务机制,其实是RocketMQ
提供了一种half
消息的机制。当订单服务接受到订单支付信息后,订单服务会发送half
消息到RocketMQ
中,这个half
消息是不被消费者所见的。怎么理解这个half
信息呢,按照我自己的理解,就是它实现了一半的消息功能,只在生产端可见,在消费端不可见。另外这个half
信息相当于一种RocketMQ
的可用性探测,如果half
消息都发送失败的话,就不必再进行下游业务的一系列操作了。
如果此时用于探测 RocketMQ 的可用性的 half 消息发送失败了,那么说明此时订单服务与 RocketMQ 存在异常,则会对之前订单进行一系列的回滚操作。如果 half 消息被成功投递,则需要进行本地事务操作,更新订单状态。
如果本地事务执行失败了怎么办,订单服务可以发送 rollback 请求,将之前的 half 消息从 RocketMQ 中进行删除,不再进行后续的消息投递。
half 消息原理分析
上文提到half
消息不被消费端可见,那么这个half
消息是怎么实现在RocketMQ
中不被积分服务所见的呢?
订单服务发送half
消息,实际并不是将消息投递到积分服务订阅的topic
,而是将消息投递到RocketMQ
中的RMQ_SYS_TRANS_HALF_TOPIC
对应的messeageQueue
。由于积分服务并没有订阅这个Topic
,所以这个消息对于积分服务是不可见的。
另外有个OP_TOPIC
用于记录对应half
消息的commit/rollback
状态。大致的交互如下如所示:
如果订单服务half
消息发送失败了,由于网络原因或者RocketMQ
挂了,那么此时需要执行一些回滚操作,让订单进行关闭。因为订单信息无法通知到下游服务了。
那么如果half
消息已经写入RocketMQ
中,但是本地事务执行失败又该怎么办呢?也就是说当订单服务接收到half
消息写入成功的响应后,更新订单信息时发生了异常,无法完成状态更新。那么此时订单服务需要发送rollback
的请求给RocketMQ
,通知其将原来的half
信息进行删除。如果本地事务执行成功,则需要发送 commit 请求给RocketMQ
,RocketMQ
会将原先存在RMQ_SYS_TRANS_HALF_TOPIC
中的消息重新投递到积分服务订阅的TOPIC
中去,这样积分服务就可以正常消费信息进行下一步的积分操作了。
再考虑一种情况,如果订单服务发送commit
或者rollback
请求未正常投递到RocketMQ
中,RocketMQ
不知道half
消息到底是对应的本地事务到底是执行成功了还是执行失败了。针对这种情况,订单服务需要提供状态回查接口,RocketMQ
定时检测是否还有没有处理的half
消息,当存在这样的消息时,RocketMQ
调用回查接口确认本地事务执行情况。执行失败的则删除half
消息,执行成功则重新投递消息。
总结
通过上文的分析,订单服务和RocketMQ
之间的交互,通过事务消息机制可以保证消息可以被可靠投递。至少在订单服务和RocketMQ
之间不会出现消息丢失的问题。
我是慕枫,感谢各位小伙伴点赞、收藏和评论,文章持续跟新,我们下期再见!
微信搜索:慕枫技术笔记,优质文章持续更新,我们有学习打卡的群可以拉你进,一起努力冲击大厂,另外有很多学习以及面试的材料提供给大家。
版权声明: 本文为 InfoQ 作者【慕枫技术笔记】的原创文章。
原文链接:【http://xie.infoq.cn/article/3bd90f676557c186319238eef】。文章转载请联系作者。
评论