29 除了使用事务消息方案,还有什么解决发送消息零丢失的方案
Posted 鮀城小帅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了29 除了使用事务消息方案,还有什么解决发送消息零丢失的方案相关的知识,希望对你有一定的参考价值。
1.事务消息方案的弊端
在RocketMQ中,生产者发送消息的时候,可能存在消息的丢失,即消息根本没有进入到MQ就丢了。
前面说过了,通过使用RocketMQ事务消息机制去发送消息到MQ,一定是可以保证消息必然发送到MQ的,不会丢。
但是,要注意的是,这一整个流程中,先得发送half消息,完了生产者还得发送rollback or commit的请求,要是中间有点什么问题,MQ还得回调生产者的接口。
这种复杂的机制很可能导致整体性能的拉低,从而导致吞吐量降低。
2.基于重试机制来确保消息到达MQ
当MQ收到消息之后写入本地磁盘文件了,即使只是写入os cache缓存的情况下,MQ就相当于是将消息写入自己本地存储了,它就会返回响应给订单系统(生产者)。
此时只要生产者在代码中发送消息到MQ之后,同步等待MQ返回响应给我们,一直等待,如果半路中有网络异常或者MQ内部异常,我们肯定会受到一个异常,比如网络错误,或者请求超时之类的。
重试发送消息
订单系统(生产者)在收到异常之后,就认为消息到MQ发送失败了,然后再次重试尝试发送消息到MQ,接着再次同步等待MQ返回响应给我们,这样一直反复重试。
如果短时间网络异常导致消息一直没法发送,那么只要不停的重试,网络一旦恢复了,消息就可以发送到MQ了。
而如果在反复重试多次后发现一直没法把消息投递到MQ,此时就可以考虑直接让订单系统(生产者)回滚之前的流程,比如发起退款流程,判定本次订单支付交易失败了。
基于这种 “同步发送消息 + 反复重试” 的方案,是可以保证消息一定可以投递到MQ中的。
3.如何保证消息发送到MQ失败时,本地事务一定回滚
(1)本地事务与推送消息逻辑分开
这里的分开是指,先执行订单本地事务,然后再发送消息到MQ去。
在上述伪代码中,先执行订单本地事务,接着发送消息到MQ,如果订单本地事务执行失败了,则不会继续发送消息到MQ了。
如果订单事务执行成功了,发送MQ失败了,自动进行几次重试,重试如果一直失败,就回滚订单事务。
问题:当刚执行完成了订单本地事务了,结果还没等生产者发送消息到MQ,此时订单系统就突然崩溃了。
这就会导致订单状态可能已经修改为了“已完成” , 但是消息却没发送到MQ去。在这种场景中,写好的多次重试发送MQ之类的代码根本没有机会执行。
而此时订单本地事务还已经执行成功了,消息没发送出去,红包系统没机会派发红包,就导致了用户支付成功了,结果看不到自己的红包。
(2)把订单本地事务和重试发送MQ消息放到一个事务代码中
在上述的伪代码中,对payOrderSuccess()方法加入了事务。那么,在这个事务方法中,即使执行了orderService.finishOrderPay(),但也只是执行了增删改SQL语句,并没有提交订单本地事务。
如果发送MQ消息失败了,而且多次重试还不成功,就抛出异常会自动回滚订单本地事务;
如果刚执行了orderService.finishOrderPay(),结果订单系统直接崩溃了,此时订单本地事务会回滚,因为根本没提交过。
这个方案可以很好的解决前面的MQ发送失败,但是订单事务还是提交了的问题。
弊端:当订单系统卡在多次重试MQ的代码那里时,可能耗费好几秒钟,此时回调通知你的系统早就等不及可能都超时异常了。
另外,把重试MQ的代码放在这个逻辑里,可能会导致订单系统的这个接口性能很差。
4. 本地事务回滚一定可靠吗
上述代码中,虽然在方法上加了事务注解,但是代码里还有更新Redis缓存和Elasticsearch数据的代码逻辑,如果订单系统已经完成了订单数据库更新、Redis缓存更新、ES数据更新,结果没发送MQ时订单系统就崩溃了。
这里的问题在于,虽然订单数据库的操作会回滚,但是Redis、Elasticsearch中的数据更新并不会自动回滚。此时数据还是会不一致。
由此可见,完全寄希望于本地事务自动回滚是不现实的。
5.保证业务系统一致性的最佳方案:基于RocketMQ的事务消息机制
真正要保证消息一定投递到MQ,同时保证业务系统之间的数据完全一致,最佳的方案还是用基于RocketMQ的事务消息机制。
基于这个方案落地之后,就可以保证订单系统的本地事务一旦成功,那么必然会投递消息到MQ去,通知红包系统去派发红包,保证业务系统的数据是一致的。
而且整个过程中,不需要进行长时间的阻塞和重试。
如果half消息发送失败了,就直接回滚整个流程。如果half消息发送成功了,后续的rollback或者commit发送失败了,也不需要自己去卡在那里反复重试,可以直接让代码结束即可,因为后续MQ会过来回调生产者的接口来判断执行rollback or commit的。
以上是关于29 除了使用事务消息方案,还有什么解决发送消息零丢失的方案的主要内容,如果未能解决你的问题,请参考以下文章