目录
1 TX-LCN框架的基本思路
2 遇到的问题记录
2.1 事务提交/回滚失败,锁表问题
2.2 事务补偿失败
微服务架构不可避免的要解决分布式事务的问题,为了避免出现分布式事务,在进行微服务划分的时候,我们一般尽量保证业务操作独立,但是有时候分布式事务又是不可避免的。业界关于分布式事务的处理方案也有几种,网上搜到比较多的就是TX-LCN框架。
官网:/zh-cn/index.html
1 TX-LCN框架的基本思路
这里借用一张官网流程图
可以看到,整体来说,每个微服务的事务还是由Spring自身处理,只不过,不管对于调用方还是被调用来说,在相关操作处理完之后,事务不会立马提交,而是挂起的状态。等到调用方处理完所有的业务操作之后,调用方会通知tm,tm会通知该事务组对应调用链上的所有tc,提交或者回滚对应的事务。
这里有几点需要注意:
(1)tm在通知tc进行事务的回滚和提交时,可能会因为网络或者超时参数配置原因出现失败,如果出现失败,tm会忘t_tx_exception表中插入一条记录,然后自动进行事务补偿,即:重试通知。
(2)TCC模式可以处理非数据库事务方式的“事务”操作,例如A调用B和C,其中C使用的是不支持事务的数据库,此时A和B使用LCN模式,C使用TCC模式,TCC模式会自己重写onConfirm和onCancel方式,即事务的提交和回滚操作需要自己定义,这就为不支持事务的数据库提供了解决方案。
下面记录下使用TX-LCN框架时候遇到的问题以及注意点。
2 遇到的问题记录
2.1 事务提交/回滚失败,锁表问题
问题描述:日志查看都正常,但是就是数据插入失败,而且后续再操作会一直表锁。
默认情况下TC是使用应用名称(spring.application.name)加端口号作为模块名称的。这种情况在单服务情况下没问题,但是如果微服务横向扩展集群部署的时候,就会有问题。例如A服务进行集群部署分为A1和A2。此时在TM上注册的模块名称有两个一模一样的记录。当TM通知的时候,会因为模块名称标识一样,出现该提交的事务没收到提交消息提醒,造成事务一直不提交,表锁的问题。
解决方法:
针对这种方式,官网也有解决办法,可以通过TC模块标识自定义方式来解决
@Componentpublic class MyModIdProvider implements ModIdProvider{@Value("${eureka.instance.ip-address}")private String ip;@Value("${server.port}")private String port;@Overridepublic String modId(){return ip+":"+port;}}
这里贴一个截图,第一条是自定义模块标识,第二条是默认的。
解决了模块标识唯一的问题,也就解决了事务通知混乱的问题,也就没有表锁了。
2.2 事务补偿失败
tc和tm之间的通信是通过tcp建立的可靠有保障的长链接,但是通讯总是难免受到网络状况的影响,当tm通知tc时候的时候,tm会自动进行事务补偿,但是测试的过程中,发现事务没有补偿,而且在管理平台上也查看不到相关异常记录,对应的t_tx_exception表中也没有记录。跟踪应用的日志发现,有包jpa的相关异常。查看源码后,发现引起该问题的主要原因是TxException表的生成策略设置的AUTO,AUTO的话主键的生成策略是JPA自己的策略。
但是对应表t_tx_exception的主键是自增,这就造成在出现异常的时候,无法把异常插入到异常表,最终导致事务补偿失败。
解决方案:
修改为IDENTITY然后重新编译下即可。