分布式事务-消息驱动模型

一句话:

分布式事务发起方和联动方割裂开来,通过记录跟分析前一个事务的运行状态,决定下一步的行为是回
滚还是开启下游事务

具体点

"割裂":
    割裂意味着每个事务是独立提交的,这是最终一致模型。从代码上来开,也不再是同一个方法内,
    通过调用开启另外的事务了。即原来的业务逻辑:
        func(){
            执行本地事务A
            执行远程事务B
        }
        
    割裂后业务逻辑:
        func(){
            执行本地事务A
            在事务A提交的同时记录事务信息
        }
        

"记录跟分析前一个事务的运行状态":
    先谈谈如何记录事务的运行状态信息。我们最容易想到的方案就是在同一个本地事务内,提交的同
    时插入一条事务状态信息数据即可。这样,只要我们保证了事务提交,必然有相关信息数据持久,
    对一个分布式事务而言,我们可用唯一的xid字段去标记,如此,无论这一组次的子事务状态数据
    是否在同一个库,我们都能轻易的找到他们。

    另外,在事务提交的同时,如果能够发送一条可靠消息,那么也能达到持久的目的,这是消息驱动
    采用的方式,但在后面会说到,发送一条可靠消息,可能并不是那么容易。


"去决定下一步的行为":
    我们不妨从分布式事务的源头来看,发起方的本地事务提交了,假如相关的带有唯一标志xid的事
    务信息被插入了数据库,这个时候,如果我们有一个独立的观察者,发现了这样的一条事务信息,
    若检查事务信息的状态是本地提交,那么观察者会根据相关的业务信息去开启下游事务逻辑,直至
    整个业务逻辑完成,或执行各自的回滚逻辑,这便是消息驱动的核心原理。
    
    思考一下,独立的观察者的实现方案。
   
    1。可以通过异步的定时任务去捞取信息
    2。可以通过订阅binlog去实现事务信息的消费
    3。借助于消息中间件MQ,这是本章将介绍的内容
    
    我们采用MQ的模式,于是整个分布式事务的逻辑变成了这样:        
    
        提交上游事务 -> 发送了一条可靠消息  -> 下游事务消费消息  -> 回滚或者继续驱动 

更进一步

消息驱动的链路逻辑清楚了,但是这条链路上的问题依旧很多,这里我们进入更深一步的讨论。

1。如何在上游事务提交的同时发送一条可靠消息。
    
    从单个事务的生命周期来看,我们无外乎在提交前跟提交后发送消息而已,假设先在MQ正常的情况
    下讨论
    
    a.如果在提交前发送消息,本地事务异常失败了,消息却被消费了,明显产生了问题。
    b.如果在提交后发送消息,jvm挂了或者网络问题,引发消息发送失败,这又产生了问题。
    
    其实解决也不复杂,我们只需要在提交前发送一种不被消费的消息,在提交后再修改这条消息的状
    态让它具备被消费的能力即可,这种功能在RocketMq被叫做事务消息,没有被确认状态的消息称为
    半消息。
    
    回头看
    问题a 被解决了,没有被确认的消息叫做半消息,不能被消费,我们借助于发送消息的ack机制,
    保证事务提交前发送成功。
    
    在看问题b 半消息发送了,本地事务提交后jvm挂掉了,怎么办? 聪明的你已经想到了,那就是和
    本地事务一同插入的,本地事务表信息!
    
    我们提供给mq一个回查机制,当半消息迟迟得不到确认,那么去回查,发现相关xid标记的本地事
    务已经插入了且状态是本地已经提交了,那么自然而然,这条半消息状态可以修改为已完成,可以
    被消费了。
    
    否则呢?否则这条消息就应该状态修改为回滚!让相关的消费服务执行回滚逻辑!
    
2。如何保证能够正确的消费掉这条事物消息
    消费方需要做到接口等幂,若多次尝试消费失败,最终到达死信队列。

简单总结

对比TCC方案,很明显,我们不要去做最难的业务资源预留的设计。
借助于中间件,提升了维护难度,还是买人家的服务来用吧。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容