一、Seata简介
2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),蚂蚁金服后在Fescar 0.4.0 版本中贡献了 TCC 模式。后来更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
Seata三大基本组件:
Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
Seata官方调用流程图:
二、Fescar相比XA二阶段优缺点:
优点:
基于SQL解析实现了自动补偿,降低业务侵入性。
第一阶段就本地事务就提交了 ,二阶段commit是异步操作相对XA两段全部持有资源更高效。
Fescar提供了两种模式,AT和MT。在AT模式下事务资源可以是任何支持ACID的数据库,在MT模式下事务资源没有限制,可以是缓存,可以是文件,可以是其他的等等。当然这两个模式也可以混用。
global lock全局锁实现了写隔离与读隔离。
Undolog日志自动清理
缺点:
代码入侵性体现为配置Fescar的数据代理和加个注解,每个业务库都需要一个Undolog表。
从调用图中开源看出性能损耗有:一条Update的SQL,获取全局事务xid(TC通讯)、before image(查询)、after image(查询)、insert undo log(Undolog表的blob字段数据量可不小)、before commit(TC通讯,判断锁冲突);为了自动补偿在Undolog表花了不小开销,而且触发概率比较低。
二阶段commit也是需要占用系统资源。
二阶段回滚需要删除各节点的Undolog才能释放全局锁。
三、实验
OrderServerApplication日志展示了事务增强拦截器GlobalTransactionalInterceptor
i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.3.2:8091:2044579200]
i.seata.sample.service.OrderServiceImpl : ------->交易开始
i.seata.sample.service.OrderServiceImpl : ------->扣减账户开始order中
i.s.core.rpc.netty.RmMessageListener : onMessage:xid=192.168.3.2:8091:2044579200,branchId=2044579202,branchType=AT,resourceId=jdbc:mysql://127.0.0.1/seat-order,applicationData=null
io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.3.2:8091:2044579200 2044579202 jdbc:mysql://127.0.0.1/seat-order
i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.3.2:8091:2044579200 branch 2044579202, undo_log deleted with GlobalFinished
io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
i.seata.tm.api.DefaultGlobalTransaction : [192.168.3.2:8091:2044579200] rollback status: Rollbacked
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Read timed out executing GET http://account-server/account/decrease?userId=1&money=100] with root cause
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_231]
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:1.8.0_231]
at java.net.SocketInputStream.read(SocketInputStream.java:171) ~[na:1.8.0_231]
at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:1.8.0_231]
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:1.8.0_231]
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:1.8.0_231]
at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:1.8.0_231]
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClien