背景
想简单体验下AT模式,搞个简单的环境试一试,使用file模式,不接入nacos,seata也不接入mysql
安装seata
docker run -d --name seata-server -h 192.168.66.48 -p 8091:8091 -p 7091:7091 -e SEATA_IP=192.168.66.48 ca13393551e3
最后面的是seata的镜像,安装的是1.6.0版本,默认账号密码seata,启动即可使用
数据库部分
测试用的两个数据库,都要新增undo表
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
程序部分
引用包
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
需要连接mysql的服务需要加注解开启数据源代理
@EnableAutoDataSourceProxy
@SpringBootApplication
@EnableDubbo
@EnableAutoDataSourceProxy
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
主服务使用@GlobalTransactional开启全局事务,timeout时间设置长一点,方便断点测试
服务上的seata配置
seata.enabled=true
seata.tx-service-group=sample-seata-test
seata.transport.type=TCP
seata.transport.server=NIO
# client和server通信心跳检测开关(默认为true)
seata.transport.heartbeat=true
seata.transport.enable-client-batch-send-request=true
seata.transport.thread-factory.boss-thread-prefix=NettyBoss
seata.transport.thread-factory.worker-thread-prefix=NettyServerNIOWorker
seata.transport.thread-factory.server-executor-thread-prefix=NettyServerBizHandler
seata.transport.thread-factory.share-boss-worker=false
seata.transport.thread-factory.client-selector-thread-prefix=NettyClientSelector
seata.transport.thread-factory.client-selector-thread-size=1
seata.transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThread
seata.transport.thread-factory.boss-thread-size=1
seata.transport.shutdown.wait=3
# client和server通信编解码方式
seata.transport.serialization=SEATA
seata.transport.compressor=none
# TC集群,需要和Seata-Server保持一致
seata.service.vgroupMapping.sample-seata-test=default
# 降级开关,默认为false,业务根据连续错误数自动降级,不走Seata事务
seata.service.enable-degrade=false
# 全局事务开关,默认为false,false为开启,true为关闭
seata.service.disable-global-transaction=false
# TC服务列表,也就是Seata服务端地址,只有当注册中心为file时使用
seata.service.grouplist.default=192.168.66.48:8091
其他的就是一个简单的dubbo调用,搞了两个更新,两个服务连了不同的mysql库
测试结果
正常流程
小demo起来后发现挺顺利,传参1跑异常回滚,两个表都没更新,传参其他的正常,两个表都更新
异常测试
在product服务更新时候,打上断点,然后修改user服务更新时候那行数据,把aaaa123改成其他的
我这里加了个更新接口单独更新了user服务的那个表,然后就报错了
user服务一直在重试归滚,后台也多了条全局锁,看日志是事务数据被污染,无法成功回滚,会一直重试,去user服务对应的mysql数据库中,删除undo_log的记录(删了两次),恢复正常
思考了一下我感觉,应该把updateUser也加入事务中
测试结果是现在获取锁超时,给更新后发起的更新操作失败了
总结
众所周知,seata的AT模式是最终一致性,测试看起来确实是,如果不对相关的操作加入事务,污染了事务数据,会导致事务回滚失败,一直回滚,在AT中就死锁了,这个应该是接入这个模式里面比较容易出现的一个坑。