在上一篇中通过阅读Seata服务端的代码,我们了解到TC是如何处理来自客户端的请求的,今天这一篇一起来了解一下客户端是如何处理TC发过来的请求的。要想搞清楚这一点,还得从GlobalTransactionScanner说起。

Seata 1.5.2 源码学习(Client端)

启动的时候,会调用GlobalTransactionScanner#initClient()方法,在initClient()中初始化TM和RM

TM初始化,主要是注册各种处理器,最终构造一个处理器映射表,不再多说

HashMap<Integer/*MessageType*/, Pair<RemotingProcessor, ExecutorService>> processorTable = new HashMap<>(32);

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

重点关注RM初始化

Seata 1.5.2 源码学习(Client端)

RM初始化过程中,设置了 resourceManager 和 transactionMessageHandler,然后也是注册各种处理器,最终也是构造一个消息类型和对应的处理器的一个映射关系

Seata 1.5.2 源码学习(Client端)

可以看到,图中上半部分是RM特有的,下半部分与TM初始化注册处理器类似

然鹅,真正处理请求的还是靠调用各个处理器中的handler.onRequest()方法,于是问题的关键就很明显了,就在于handler

1. ResourceManager

在了解ResourceManager之前,让我们首先了解一下ResourceManagerInbound和ResourceManagerOutbound

ResourceManagerInbound是处理接收到TC的请求的,是TC向RM发请求

Seata 1.5.2 源码学习(Client端)

ResourceManagerOutbound是处理流出的消息的,是RM向TC发请求

Seata 1.5.2 源码学习(Client端)

ResourceManager继承了二者,所以既负责向TC发请求,又负责接收从TC来的请求。

还记得刚才在RMClient中是怎么获取ResourceManager的吗?是调用DefaultResourceManager.get()获取的

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

DefaultResourceManager.get()得到的是一个单例DefaultResourceManager,创建DefaultResourceManager的时候会构建一个分支类型与ResourceManager的一个Map

Seata 1.5.2 源码学习(Client端)

2. TransactionMessageHandler

TransactionMessageHandler负责处理接收到的RPC消息

Seata 1.5.2 源码学习(Client端)

前面在 RMClient 中通过 DefaultRMHandler.get() 获取 TransactionMessageHandler

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

3. 消息处理

Seata 1.5.2 源码学习(Client端)

RMClient#init()的时候new了一个RmNettyRemotingClient

Seata 1.5.2 源码学习(Client端)

这里要记住,rmNettyRemotingClient的两个成员变量此时已经被赋值了:

  • resourceManager是DefaultResourceManager,
  • transactionMessageHandler是DefaultRMHandler

Seata 1.5.2 源码学习(Client端)

RmNettyRemotingClient构造方法中调用父类AbstractNettyRemotingClient的构造方法

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

可以看到,根据收到的RPC消息类型,从processorTable中获取对应的Processor,最后调用对应RemotingProcessor的process()方法进行处理消息

RemotingProcessor的实现类很多,挑其中一个RmBranchCommitProcessor看一下

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

真相大白,最终还是调DefaultRMHandler#handle()

捋一下这个过程

Seata 1.5.2 源码学习(Client端)

最后,补充一个,this为什么是DefaultRMHandler

Seata 1.5.2 源码学习(Client端)

补充二:AbstractTransactionRequestToRM

Seata 1.5.2 源码学习(Client端)

4. 分支事务提交(二阶段)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

交给AsyncWorker去执行

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

可以看到:

  1. 封装成一个Phase2Context对象,并将其放入队列中
  2. 如果放入成功,则立即返回提交成功,后续交由定时任务执行
  3. 如果放入失败,则主动触发定时任务先执行一次,以便腾出空间来,待执行完后,队列里面就有空间了,再将任务放入队列,等待下一次定时任务执行
  4. 定时任务1秒执行一次,执行的时候将队列中的任务取出,然后循环遍历分段执行
  5. 执行的过程就是删除对应事务的undo log
  6. 如果过程中抛异常,则将任务再放回队列中

所以,RM收到TC发的提交指令后,仅仅只是删除该事务的undo_log表记录

Seata 1.5.2 源码学习(Client端)

5. 分支事务回滚(二阶段)

与提交类似

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

Seata 1.5.2 源码学习(Client端)

所以,回滚就是根据事务的undo_log进行回滚

6. 总结

1、启动时,自动代理数据源,应用GlobalTransactionalInterceptor,初始化TM和RM

2、进入@GlobalTransactional业务方法时,TM向TC发请求申请开启全局事务,并获得全局事务ID

3、业务方法调用远程服务接口完成业务处理

4、RM执行本地逻辑,注册分支事务,获取全局锁,成功后提交本地事务并写入undo_log,本地事务提交成功后向TC报告分支事务

5、TM发起全局事务提交请求,TC向所有已注册的RM发请求,让RM进行分支提交,删除本地undo_log

6、若执行失败,TM发起全局事务回滚,TC向所有RM发请求,回滚分支事务,还原数据

Seata 1.5.2 源码学习(Client端)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。