Seata使用AT模式及源码解析(二)

五.源码启动seata服务

1.修改registry.conf配置

Seata使用AT模式及源码解析(二)_第1张图片

修改registry.conf,配置registry和config都使用nacos形式

2.启动seata服务

Seata使用AT模式及源码解析(二)_第2张图片

3.无法启动解决

  • seata在1.3.0版本中maven版本需要在3.6.0及以上
  • 项目构建无法找到io.seata.serializer.protobuf.generated下的包idea需要安装Protobuf Support插件安装时可能会搜索不到需要通过zip包安装

Seata使用AT模式及源码解析(二)_第3张图片

  • 在seata父pom.xml可能会包有些jar包无法下载需要手动下载jar手动安装到maven本地库mvn install:install-file -DgroupId=com.baidu -DartifactId=ueditor -Dversion=1.0.0 -Dpackaging=jar -Dfile=xxxx.jar
  • 插件安装完毕pom不报错后需要通过插件编译生成不存在的java类运行protobuf:compile,编译所有.proto文件

Seata使用AT模式及源码解析(二)_第4张图片

Seata使用AT模式及源码解析(二)_第5张图片

编译好后,会生成对应的java文件

Seata使用AT模式及源码解析(二)_第6张图片

4.运行server后,seata成功启动注册到nacos上这时在启动三个订单服务事务依旧可以解决

六.源码解析

对于seata源码的研究主要看seata如何拦截业务SQL生成undo_log数据如何在一阶段完成后提交全局事务如何在一阶段业务失败后通过undo_log回滚事务进行事务补偿

seata也是与spring整合使用的结合SpringBootseata也是做了一些自动配置

Seata使用AT模式及源码解析(二)_第7张图片

SeataAutoConfiguration就是seata自动配置类,发现其中向Spring容器添加了一个GlobalTransactionScanner Bean这个就是源码的入口了

Seata使用AT模式及源码解析(二)_第8张图片

Seata使用AT模式及源码解析(二)_第9张图片

GlobalTransactionScanner 实现了InitializingBean, ApplicationContextAware,DisposableBean三个接口熟悉Spring的知道InitializingBean提供afterPropertiesSet方法表示bean初始化完成后调用此方法做一些初始化操作ApplicationContextAware提供setApplicationContext方法通过此方法会向bean中注入ApplicationContext上下文对象,DisposableBean提供destroy方法是bean销毁调用的方法

Seata使用AT模式及源码解析(二)_第10张图片

afterPropertiesSet方法中最终调用initClient()方法

Seata使用AT模式及源码解析(二)_第11张图片

里面对TmClient,RmClient进行了初始化(参数就是配置文件里配置的applicationId和txServiceGroup),并注册了一个Spring的ShutdownHook钩子函数

1.TMClient的初始化

Seata使用AT模式及源码解析(二)_第12张图片

启动了一个定时器不断进行重连操作调用clientChannelManager.reconnect方法进行重连

Seata使用AT模式及源码解析(二)_第13张图片

根据transactionServiceGroup获取seata-server的ip地址列表,然后进行重连

Seata使用AT模式及源码解析(二)_第14张图片

RegistryFactory.getInstance().lookup(transactionServiceGroup);是对不同注册中心做了适配的,默认看下Nacos形式的实现

Seata使用AT模式及源码解析(二)_第15张图片

根据事务分组找到分组所属的server集群名称,这里是default然后根据集群名称找到server对应ip端口地址

Seata使用AT模式及源码解析(二)_第16张图片

Seata-server的IP地址已获取到

Seata使用AT模式及源码解析(二)_第17张图片

最后将获取到的seata-server的IP地址放到Netty中封装TmClient就初始化完毕

TmClient初始化总结

  • 启动定时器,尝试进行一次重连seata-server
  • 重连时,先从nacos或则其他配置中根据分组名称(service_group)找到集群名称(cluster_name)
  • 再根据集群名称找到集群ip端口列表
  • 从ip列表中选择一个用netty进行连接

2.RMClient初始化

Seata使用AT模式及源码解析(二)_第18张图片

设置资源管理器resourceManager,设置消息回调监听器用于接收TC在二阶段发出的提交或者回滚请求Seata中对ResourceManager,AbstractRMHandler做了SPI适配,以ResouceManager为例

Seata使用AT模式及源码解析(二)_第19张图片

可以看到初始化DefaultResouceManager时会使用ClassLoader去加载对应Jar下的实现,而默认AT模式使用的实现是数据库,也就是rm-datasource包下的实现,找实现类路径需要定位到/resources/META-INF/扩展接口全路径去找就会找到对应的实现类

Seata使用AT模式及源码解析(二)_第20张图片

Seata使用AT模式及源码解析(二)_第21张图片

ResourceManager对应实现类全 io.seata.rm.datasource.DataSourceManager,该类中指定了了提交和回滚的方法,DefaultRMHandler对应实现类全路径io.seata.rm.RMHandlerAT,是个接收server消息并做对应提交或者回滚操作的回调处理类

RMClinet的init()方法与TMClient基本一致

总结

  • Spring启动时,初始化了2个客户端TmClient、RmClient
  • TmClient与seata-server通过Netty建立连接并发送消息
  • RmClient与seata-server通过Netty建立连接,负责接收二阶段提交、回滚消息并在回调器(RmHandler)中做处理

3.AT一阶段开启全局事务

在需要进行全局事务管理的接口上会加@GlobalTransactional注解这个注解会又一个对应的拦截器进行拦截GlobalTransactionalInterceptor,invoke就是拦截方法

Seata使用AT模式及源码解析(二)_第22张图片

  • 如果方法上有全局事务注解,调用handleGlobalTransaction开启全局事务
  • 如果方法上有全局锁注解,调用handleGlobalLock开启全局
  • 如果啥都没有,按普通方法执行,提升性能

Seata使用AT模式及源码解析(二)_第23张图片

handleGlobalTransaction调用了transactionalTemplate.execute方法

Seata使用AT模式及源码解析(二)_第24张图片

  • 开启全局事务beginTransaction
  • 执行业务方法business.execute()
  • 出现异常执行completeTransactionAfterThrowing回滚
  • 没有异常提交事务commitTransaction

开启全局事务最终调用io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.String)方法

Seata使用AT模式及源码解析(二)_第25张图片

请求seata-server获取全局事务XID

Seata使用AT模式及源码解析(二)_第26张图片

Seata使用AT模式及源码解析(二)_第27张图片

将XID绑定在RootContext中由此可以看出全局事务是由TM发起的,TM发起全局事务请求给seata-server服务seata-server服务接受到请求后处理以下是seata服务代码):

Seata使用AT模式及源码解析(二)_第28张图片

io.seata.server.coordinator.DefaultCoordinator#doGlobalBegin方法接受客户端开启全局事务的请求调用io.seata.server.coordinator.DefaultCore#begin开启全局事务

Seata使用AT模式及源码解析(二)_第29张图片

通过当前会话开启

Seata使用AT模式及源码解析(二)_第30张图片

Seata使用AT模式及源码解析(二)_第31张图片

实则调用io.seata.server.session.AbstractSessionManager#onBegin方法又调用io.seata.server.storage.db.session.DataBaseSessionManager#addGlobalSession方法

Seata使用AT模式及源码解析(二)_第32张图片

这里往数据库里写入数据

Seata使用AT模式及源码解析(二)_第33张图片

Seata使用AT模式及源码解析(二)_第34张图片

Seata使用AT模式及源码解析(二)_第35张图片

这里向seata库global_tab插入数据到此全局事务已开启

4.AT一阶段执行业务SQL

全局事务已开启下面需要执行业务SQL生成undo_log数据,全局事务拦截成功后最终还是执行了业务方法的,但是由于Seata对数据源做了代理,所以sql解析与undo_log入库操作是在数据源代理中执行的,代理就是Seata对DataSource,Connection,Statement做的代理封装类

Seata使用AT模式及源码解析(二)_第36张图片

项目中使用的数据源均用seata的DataSourceProxy代替

Seata使用AT模式及源码解析(二)_第37张图片

最终对Sql进行解析操作,发生在StatementProxy类中

Seata使用AT模式及源码解析(二)_第38张图片

Seata使用AT模式及源码解析(二)_第39张图片

Seata使用AT模式及源码解析(二)_第40张图片

  • 先判断是否开启了全局事务,如果没有,不走代理,不解析sql,提升性能
  • 调用SQLVisitorFactory对目标sql进行解析
  • 针对特定类型sql操作(INSERT,UPDATE,DELETE,SELECT_FOR_UPDATE)等进行特殊解析
  • 执行sql并返回结果

不同类型的SQL处理方法不一样这里以insert为例

Seata使用AT模式及源码解析(二)_第41张图片

insert使用的是InsertExecutor.execute方法但其实最终还是使用io.seata.rm.datasource.exec.BaseTransactionalExecutor#execute方法

Seata使用AT模式及源码解析(二)_第42张图片

将上下文中的xid绑定到了statementProxy中,并调用了doExecute方法,看下AbstractDMLBaseExecutor中的doExecute方法

Seata使用AT模式及源码解析(二)_第43张图片

方法中调用了executeAutoCommitTrue/executeAutoCommitFalse

Seata使用AT模式及源码解析(二)_第44张图片

但仔细发现最终都是调用executeAutoCommitFalse方法

Seata使用AT模式及源码解析(二)_第45张图片

Seata使用AT模式及源码解析(二)_第46张图片

获取beforeImage数据

Seata使用AT模式及源码解析(二)_第47张图片

执行业务sql还是使用com.alibaba.druid.pool.DruidPooledPreparedStatement#execute方法执行

Seata使用AT模式及源码解析(二)_第48张图片

获取afterImage

Seata使用AT模式及源码解析(二)_第49张图片

Seata使用AT模式及源码解析(二)_第50张图片

Seata使用AT模式及源码解析(二)_第51张图片

在提交事务是插入undo_log日志

Seata使用AT模式及源码解析(二)_第52张图片

Seata使用AT模式及源码解析(二)_第53张图片

提交事务向seata-server注册分支信息seata-server接收到请求seata源码

Seata使用AT模式及源码解析(二)_第54张图片

io.seata.server.coordinator.DefaultCoordinator#doBranchRegister方法

Seata使用AT模式及源码解析(二)_第55张图片

Seata使用AT模式及源码解析(二)_第56张图片

io.seata.server.storage.db.session.DataBaseSessionManager#addBranchSession方法

Seata-server添加分支信息完成,到这里,一阶段结束业务数据undo_log分支信息都已经写入数据库

5.AT二阶段提交

Seata使用AT模式及源码解析(二)_第57张图片

commitTransaction(tx);跟进

Seata使用AT模式及源码解析(二)_第58张图片

Seata使用AT模式及源码解析(二)_第59张图片

Seata使用AT模式及源码解析(二)_第60张图片

Seata使用AT模式及源码解析(二)_第61张图片

最终通过TM请求seata-server,Seata-server接收到全局提交请求seata源码

Seata使用AT模式及源码解析(二)_第62张图片

Seata使用AT模式及源码解析(二)_第63张图片

Seata使用AT模式及源码解析(二)_第64张图片

Seata使用AT模式及源码解析(二)_第65张图片

 

Seata-server接收到客户端全局提交请求后先回调客户端删除undo_logseata在删除分支及全局事务

之前说过RMClient在初始化时设置资源管理器resourceManager,置消息回调监听器用于接收TC在二阶段发出的提交或者回滚请求

Seata使用AT模式及源码解析(二)_第66张图片

Seata-server删除分支数据及全局事务数据

Seata使用AT模式及源码解析(二)_第67张图片

Seata使用AT模式及源码解析(二)_第68张图片

Seata使用AT模式及源码解析(二)_第69张图片

客户端删除undo_log数据

Seata使用AT模式及源码解析(二)_第70张图片

Seata使用AT模式及源码解析(二)_第71张图片

getResourceManager获取的就是RMClient初始化时设置的资源管理器DataSourceManager

Seata使用AT模式及源码解析(二)_第72张图片

Seata使用AT模式及源码解析(二)_第73张图片

这边只是往一个ASYNC_COMMIT_BUFFER缓冲List中新增了一个二阶段提交的context但真正提交在AsyncWorker的init()方法

Seata使用AT模式及源码解析(二)_第74张图片

Seata使用AT模式及源码解析(二)_第75张图片

Seata使用AT模式及源码解析(二)_第76张图片

删除Undo_log

6.AT二阶段回滚

二阶段回滚seata-server端代码与二阶段提交类似这里省略

Seata使用AT模式及源码解析(二)_第77张图片

主要看回滚客户端如何进行事务补偿

Seata使用AT模式及源码解析(二)_第78张图片

Seata使用AT模式及源码解析(二)_第79张图片

最终回滚方法调用的是UndoLogManager.undo(dataSourceProxy, xid, branchId);

Seata使用AT模式及源码解析(二)_第80张图片

Seata使用AT模式及源码解析(二)_第81张图片

Seata使用AT模式及源码解析(二)_第82张图片

执行回滚sql

Seata使用AT模式及源码解析(二)_第83张图片

判断undolog是否存在,存在则删除对应undolog,并一提交,到此seata的AT模式源码解析完毕。

你可能感兴趣的:(java,微服务/分布式,java,seata,源码解析)