09.分布式事务


Seata架构

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

一个分布式事务的生命周期:
1. TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。
2. RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
3. TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
4. TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

分库分表全局事务(ShardingSphere)

不能简单的在全局事务发起方使用 @GlobalTransactional,因为seata找不到逻辑表对应的物理表,需要融入 Apache ShardingSphere 分布式事务

整合 Seata AT 事务时,需要将 TM,RM 和 TC 的模型融入 Apache ShardingSphere 的分布式事务生态中。 在数据库资源上,Seata 通过对接 DataSource 接口,让 JDBC 操作可以同 TC 进行远程通信。 同样,Apache ShardingSphere 也是面向 DataSource 接口,对用户配置的数据源进行聚合。 因此,将 DataSource 封装为 基于Seata 的 DataSource 后,就可以将 Seata AT 事务融入到 Apache ShardingSphere的分片生态中。

  1. 引入依赖
  2. 配置seata.conf
    client {
        application.id = tulingmall‐order‐curr
        transaction.service.group = tuling‐order‐group
    }
  3. 开启全局事务配置:
    // GlobalTransactional和ShardingTransactionType不能同时出现
     @ShardingTransactionType(TransactionType.BASE)
     @Transactional
  4. 关闭Seata数据源自动代理
    seata:
        enable‐auto‐data‐source‐proxy: false 

柔性事务:可靠消息最终一致性

本地消息表方案

  1. 定时任务扫描日志
    • 启动独立的线程,定时对消息日志表中的消息进行扫描并发送至消息中间件,在消息中间件反馈发送成功后删除该消息日志,否则等待定时任务下一周期重试。
  2. 消费消息(幂等控制)
    • 可以使用MQ的ack(即消息确认)机制,消费者监听MQ,如果消费者接收到消息并且业务处理完成后向MQ发送ack(即消息确认),此时说明消费者正常消费消息完成,MQ将不再向消费者推送消息,否则消费者会不断重试向消费者来发送消息。
    • 由于消息会重复投递,需要实现幂等性。

Rocketmq事务消息

RocketMQ事务消息设计则主要是为了解决Producer端的消息发送与本地事务执行的原子性问题,RocketMQ的设计中broker与producer端的双向通信能力,使得broker天生可以作为一个事务协调者存在;而RocketMQ本身提供的存储机制为事务消息提供了持久化能力;RocketMQ的高可用机制以及可靠消息设计则为事务消息在系统发生异常时依然能够保证达成事务的最终一致性。在RocketMQ 4.3后实现了完整的事务消息,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部,解决Producer端的消息发送与本地事务执行的原子性问题。

  1. Producer发送事务消息
    • Producer(MQ发送方)发送事务消息至MQ Server,MQ Server将消息状态标记为Prepared(预览状态),注意此时这条消息消费者(MQ订阅方)是无法消费到的。
  2. MQ Server回应消息发送成功
    • MQ Server接收到Producer发送的消息则回应发送成功表示MQ已接收到消息。
  3. Producer执行本地事务
  4. 消息投递
    • 若Producer本地事务执行成功则自动向MQ Server发送commit消息
    • MQ Server接收到commit消息后将“该消息”状态标记为可消费,此时MQ订阅方即正常消费消息;
    • 若Producer 本地事务执行失败则自动向MQ Server发送rollback消息,MQ Server接收到rollback消息后将删除“该消息”。
    • MQ订阅方消费消息,消费成功则向MQ回应ack,否则将重复接收消息。这里ack默认自动回应,即程序执行正常则自动回应ack。
  5. 事务回查
    • 如果执行Producer端本地事务过程中,执行端挂掉,或者超时,MQ Server将会不停的询问同组的其他Producer来获取事务执行状态,这个过程叫事务回查。MQ Server会根据事务回查结果来决定是否投递消息。

对用户则来说,用户需要分别实现本地事务执行以及本地事务回查方法(RocketMQLocalTransactionListener),因此只需关注本地事务的执行状态即可。

总结梳理

Seata架构:TC、TM、RM

1. TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。
2. RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
3. TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
4. TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

分库分表全局事务(ShardingSphere)

  1. 配置seata.conf

    •   client {
            application.id = tulingmall‐order‐curr
            transaction.service.group = tuling‐order‐group
        }
      
  2. 开启全局事务配置

    • @ShardingTransactionType(TransactionType.BASE)

    • @Transactional

  3. 关闭Seata数据源自动代理

    • seata.enable‐auto‐data‐source‐proxy=false

柔性事务:可靠消息最终一致性

本地消息表方案

  1. 定时任务扫描日志

    • 启动独立的线程,定时对消息日志表中的消息进行扫描并发送至消息中间件,在消息中间件反馈发送成功后删除该消息日志,否则等待定时任务下一周期重试。
  2. 消费消息(幂等控制)

    • 使用MQ的ack(即消息确认)机制,消费者监听MQ,如果消费者接收到消息并且业务处理完成后向MQ发送ack(即消息确认),此时说明消费者正常消费消息完成,MQ将不再向消费者推送消息,否则消费者会不断重试向消费者来发送消息。

Rocketmq事务消息

解决Producer端的消息发送与本地事务执行的原子性问题

  1. Producer发送事务消息

    • Producer(MQ发送方)发送事务消息至MQ Server,MQ Server将消息状态标记为Prepared(预览状态),注意此时这条消息消费者(MQ订阅方)是无法消费到的。
  2. MQ Server回应消息发送成功

    • MQ Server接收到Producer发送的消息则回应发送成功表示MQ已接收到消息。
  3. Producer执行本地事务

  4. 消息投递

    • 若Producer本地事务执行成功则自动向MQ Server发送commit消息

    • MQ Server接收到commit消息后将“该消息”状态标记为可消费,此时MQ订阅方即正常消费消息;

    • 若Producer 本地事务执行失败则自动向MQ Server发送rollback消息,MQ Server接收到rollback消息后将删除“该消息”。

    • MQ订阅方消费消息,消费成功则向MQ回应ack,否则将重复接收消息。这里ack默认自动回应,即程序执行正常则自动回应ack。

  5. 事务回查

    • 如果执行Producer端本地事务过程中,执行端挂掉,或者超时,MQ Server将会不停的询问同组的其他Producer来获取事务执行状态,这个过程叫事务回查。MQ Server会根据事务回查结果来决定是否投递消息。
  6. 需要分别实现本地事务执行以及本地事务回查方法

    • RocketMQLocalTransactionListener

文章作者: 钱不寒
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 钱不寒 !
  目录