本地事务执行成功之后再调用其他服务,如果成功了就将消息表里的消息状态改为成功,如果失败了,则由定时任务去读取本地事务表中未成功的消息,再去调用相应的服务,成功后再次修改状态。
这里也要设置重试机制,一旦有实在不成功的,还需人工介入。这里要注意的是,也要保证对应服务的方法幂等性。
可以看出,本地消息表实现比较简单,是一种最大努力通知思想,实现的是最终一致性,容忍了数据暂时不一致的情况。
缺点是严重依赖数据库。
5、可靠消息最终一致性方案
在上面的本地消息表方案中,生产者需要额外创建消息表,还需要对本地消息表进行轮询,业务负担较重。阿里开源的 RocketMQ 4.3 之后的版本正式支持事务消息,该事务消息本质上是把本地消息表放到 RocketMQ 上,解决生产端的消息发送与本地事务执行的原子性问题。
服务 A,先给 Broker (消息中间件) 发送一个 Half Message(半消息),其实这个半消息已发送到 Broker 端,但是此消息的状态被标记为"不能投递",消费者还看不到,处于这种状态下的消息称为半消息。
发送完 半消息后,服务A 执行业务操作(本地事务),再根据操作结果:如果成功,则向 Broker 发送 一个 Commit 命令,这时半消息就变成了可以被消费者消息;如果失败,则发送一个 RollBack 命令,该消息则会被删除。
如果是 Commit 那么服务 B 就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。
如果 RocketMQ 没有收到服务 A 确认状态的消息,那么半消息 RocketMQ 会自动定时轮询回调你的接口,询问这个处理的处理情况。借助这点,服务A实现一个回调,根据实际处理结果 Commit 或者 Rollback,加强一致性判断。
在服务 B 执行的过程中也可能会失败,这时也是需要重试,一直执行不成功也需要人工介入,同时也需要保证服务 B 方法的幂等性。
6、最大努力通知最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。
就本地消息表来说会有后台任务定时去查看未完成的消息,然后去调用对应的服务,当一个消息多次调用都失败的时候可以记录下然后引入人工,或者直接舍弃。这其实算是最大努力了
事务消息也是一样,当半消息被commit了之后确实就是普通消息了,如果订阅者一直不消费或者消费不了则会一直重试,到最后进入死信队列。其实这也算最大努力。
最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。
总结其实分布式事务解决方案还有很多,但是各自还是会存在很多问题,极端情况下也都需要人工去处理,而且大大提高了流程的复杂度,会带来很多额外的开销。
所以谨记,在真实的开发过程中,能不使用分布式事务就不要使用!
后面会给大家带来分布式事务的实战,没点关注的可以点个关注,防止走丢了。