代码详细地址:https://github.com/seata/seata-samples/tree/master/dubbo。
整个seata-samples代码库很大,本例只用到了dubbo子模块。
本业务以购买商品的业务逻辑为例子。整个业务涉及到3个微服务一起提供服务:
我们只需要使用一个 @GlobalTransactional
注解在业务方法上:
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
......
}
备注:
1、每个微服务都与seata服务连接起来(可以看每个服务的xxx-service.xml)。如dubbo-storage-service.xml文件。其他服务也一样
<bean id="storageDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy"> <constructor-arg ref="storageDataSource" /> bean> <bean name="storageDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${jdbc.storage.url}"/> <property name="username" value="${jdbc.storage.username}"/> <property name="password" value="${jdbc.storage.password}"/> <property name="driverClassName" value="${jdbc.storage.driver}"/> bean>
2、每个服务都注册了一个
GlobalTransactionScanner
,字符串dubbo-demo-storage-service表示应用ID(applicationId),字符串my_test_tx_group表示事务服务组(txServiceGroup)<bean class="io.seata.spring.annotation.GlobalTransactionScanner"> <constructor-arg value="dubbo-demo-storage-service"/> <constructor-arg value="my_test_tx_group"/> bean>
3、字符串my_test_tx_group还在项目的
file.conf
文件中还用到了。
建议参考代码https://github.com/seata/seata-samples/tree/master/dubbo的readme.md文件。
略
undo_log表是临时中间表,存储的是分布式事务过程中每张表变更前后的值。一条记录代表某张表变更前和变更后的详细数据。这张表的作用是需要读者理解的。
之所以记录变更前后的详细数据,就是为了在事务失败时能够进行回滚。
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
略
# 启动seata服务
docker run --name seata-file -p 8091:8091 hellowoodes/seata:0.9.0-file
# 启动zooekper
docker run --name zookeeper -p 2181:2181 -p 2888:2888 -p 3888:3888 -d zookeeper
注意:作者下载代码测试的时候,以下应用都不能启动,后查阅资料,发现是缺少了jar包,请在启动出问题的时候在pom.xml中添加jar包。
<dependency> <groupId>com.alibaba.springgroupId> <artifactId>spring-context-supportartifactId> <version>1.0.11version> dependency>
一共设计到3张表,初始状态下,库存storage_tbl存储了100个库存,订单表order_tbl为空,账户表account_tbl有999元。测试类模拟了用户购买了2件200元的商品。
如果一切正常,那么:
1、account_tbl表账户余额为999 - 200*2 = 599元
2、order_tbl表会生成一个订单
3、storage_tbl表的库存为100 - 2 = 98
测试结果如下图:
在业务方法上加了@GlobalTransactional
注解,如果分布式事务生效,那么在发生异常Exception时,3张表的数据都会回滚,值还是初始值,即:
1、account_tbl表为999元
2、order_tbl是空的
3、storage_tbl是100件
添加模拟发生异常的代码:
查看测试结果如下图:
观察到控制台发生了一场,且数据库中3张表的数据没有变动。
全部3个微服务全部debug启动,且基于架构图,Account服务是最后调用的,在该服务上打上一个断点。然后去观察undo_log表中的内容。注意超时时间哦。
1、打上断点
在io.seata.samples.dubbo.service.impl.OrderServiceImpl#create的最后位置打上断点
2、观察undo_log表
备注:刚好3条记录,对应3张表变更前后的内容;blob的内容查看需要再navicat中设置一下;然后复制格式化成json。
3、观察3张表的数据
下面是账户余额表account_tbl变更前后的数据情况(json格式化后):
{
"@class": "io.seata.rm.datasource.undo.BranchUndoLog",
"xid": "172.17.0.4:8091:2151716199",
"branchId": 2151716201,
"sqlUndoLogs": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.undo.SQLUndoLog",
"sqlType": "UPDATE",
"tableName": "account_tbl",
"beforeImage": {
"@class": "io.seata.rm.datasource.sql.struct.TableRecords",
"tableName": "account_tbl",
"rows": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.sql.struct.Row",
"fields": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "id",
"keyType": "PRIMARY_KEY",
"type": 4,
"value": 2
},
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "money",
"keyType": "NULL",
"type": 4,
"value": 999
}
]
]
}
]
]
},
"afterImage": {
"@class": "io.seata.rm.datasource.sql.struct.TableRecords",
"tableName": "account_tbl",
"rows": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.sql.struct.Row",
"fields": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "id",
"keyType": "PRIMARY_KEY",
"type": 4,
"value": 2
},
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "money",
"keyType": "NULL",
"type": 4,
"value": 599
}
]
]
}
]
]
}
}
]
]
}
下面是订单表order_tbl:
{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.17.0.4:8091:2151716242","branchId":2151716245,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"order_tbl","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"order_tbl","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"order_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":6},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"user_id","keyType":"NULL","type":12,"value":"U100001"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"commodity_code","keyType":"NULL","type":12,"value":"C00321"},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"money","keyType":"NULL","type":4,"value":400}]]}]]}}]]}
下面是库存表storage_tbl:
{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.17.0.4:8091:2151716242","branchId":2151716243,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"UPDATE","tableName":"storage_tbl","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":100}]]}]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":2},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":98}]]}]]}}]]}