验证分布式事务问题及日志分析

下面结合实际的代码,验证 10.1.3 节中出现的分布式问题是否还存在。在验证前一定要确保所有服务都正常启动,并且服务实例与 Seata Server 之间的通信正常。

修改 OrderService.saveOrder() 方法,添加一行在执行时会出现异常的代码,如下所示:

@Transactional
@GlobalTransactional
public Boolean saveOrder(int cartId) {

    // 调用购物车服务-获取即将操作的 goods_id
    int goodsId = newBeeShopCartDemoService.getGoodsId(cartId);
    // 调用商品服务-减库存
    Boolean goodsResult = newBeeGoodsDemoService.deStock(goodsId);
    // 调用购物车服务-删除当前购物车数据
    Boolean cartResult = newBeeShopCartDemoService.deleteItem(cartId);
    // 执行下单逻辑
    if (goodsResult && cartResult) {
        // 向订单表中新增一条记录
        int orderResult = jdbcTemplate.update("insert into tb_order('cart_id') value ('" + cartId + "')");
        // 此处出现了异常
        int i=1/0;
        if (orderResult > 0) {
            return true;
        }
        return false;
    }
    return false;
}

之后重启订单服务并确认该服务与 Nacos ServerSeata Server 通信成功,再请求如下测试地址:

http://localhost:8167/order/saveOrder?cartId=10

此时的结果与 10.1.3 节中的结果相同,控制台中出现了异常信息,生成订单的接口没有返回成功的响应。控制台报出的异常信息如下:

java.lang.ArithmeticException: / by zero
    at ltd.order.cloud.newbee.service.OrderService.saveOrder(OrderService.java:51) ~[classes/:na]

数据库中的结果却不一样了,10.1.3 节中的结果是订单数据没有新增,但是购物车数据被删除了,商品库存也扣减成功了。而整合 Seata 后,一切数据正常,订单数据没有新增,购物车数据未被删除,商品库存也未扣减成功。

明明已经调用了删除购物车的接口和扣减商品库存的接口,为什么数据库中的商品数据和购物项数据没变呢?分别查看这两个服务实例的控制台输出日志,内容如下。

商品服务的控制台输出日志:

2023-05-26 18:07:56.872 INFO 31232 --- [ch_RMROLE_1_2_8]
i.s.c.r.p.c.RmBranchRollbackProcessor: rm handle branch rollback
process:xid=192.168.110.31:8092:6800691518082474159,branchId=68006915180
82474168,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/test_distr
ibution_goods_db,applicationData=null
2023-05-26 18:07:56.872 INFO 31232 --- [ch_RMROLE_1_2_8]
io.seata.rm.AbstractRMHandler: Branch Rollbacking:
192.168.110.31:8092:6800691518082474159 6800691518082474168
jdbc:mysql://127.0.0.1:3306/test_distribution_goods_db
2023-05-26 18:07:57.834 INFO 31232 --- [ch_RMROLE_1_2_8]
i.s.r.d.undo.AbstractUndoLogManager: xid
192.168.110.31:8092:6800691518082474159 branch 6800691518082474168,
undo log deleted with GlobalFinished
2023-05-26 18:07:57.986 INFO 31232 --- [ch_RMROLE_1_2_8]
io.seata.rm.AbstractRMHandler: Branch Rollbacked result:
PhaseTwo_Rollbacked

购物车服务的控制台输出日志:

2023-05-26 18:07:55.419 INFO 31246 --- [ch_RMROLE_1_2_8]
i.s.c.r.p.c.RmBranchRollbackProcessor: rm handle branch rollback
process:xid=192.168.110.31:8092:6800691518082474159,branchId=68006915180
82474172,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/test_distr
ibution_cart_db,applicationData=null
2023-05-26 18:07:55.419 INFO 31246 --- [ch_RMROLE_1_2_8]
io.seata.rm.AbstractRMHandler: Branch Rollbacking:
192.168.110.31:8092:6800691518082474159 6800691518082474172
jdbc:mysql://127.0.0.1:3306/test_distribution_cart_db
2023-05-26 18:07:56.379 INFO 31246 --- [ch_RMROLE_1_2_8]
i.s.r.d.undo.AbstractUndoLogManager: xid
192.168.110.31:8092:6800691518082474159 branch 6800691518082474172,
undo_log deleted with GlobalFinished
2023-05-26 18:07:56.545 INFO 31246 --- [ch_RMROLE_1_2_8]
io.seata.rm.AbstractRMHandler: Branch Rollbacked result: PhaseTwo Rollbacked

这两个微服务实例都在 Seata 组件的协调下完成了如下的关键步骤。

  1. rm handle branch rollback process:RM 执行回滚流程。

  2. Branch Rollbacking:分支事务正在回滚。

  3. PhaseTwo_Rollbacked:分支事务回滚完成。

由于代码中出现了异常,因此导致全局事务没有执行成功,对应的分支事务都回滚了,并没有真正地执行落库操作。

查看 Seata Server 可以看到更为详细的日志信息,内容如下(笔者在日志中添加了一些中文注释):

# 1.开启全局事务 xid:192.168.110.31:8092:6800691518082474159
# 2.开启扣减库存事务 branchId = 6800691518082474168

18:07:52.029 INFO --- [batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler: SeataMergeMessage xid=192.168.110.31:8092:6800691518082474159, branchType=AT, resourceId=jdbc:mysql://127.0.0.1:3306/test_distribution_goods_db, lockKey=tb_goods:202 5, clientIp:192.168.110.31, vgroup:test_save_order_group
18:07:53.117 INFO --- [verHandlerThread 1 48 500] i.seata.server.coordinator.AbstractCore: Register branch successfully, xid = 192.168.110.31:8092:6800691518082474159, branchId = 6800691518082474168, resourceId = jdbc:mysql://127.0.0.1:3306/test_distribution_goods_db, lockKeys = tb_goods:2025

# 3.开启删除购物项事务 branchId = 6800691518082474172

18:07:53.653 INFO --- [batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler: SeataMergeMessage xid=192.168.110.31:8092:6800691518082474159, branchType=AT, resourceId=jdbc:my sql://127.0.0.1:3306/test_distribution_cart_db, lockKey=tb_cart_item:10, clientIp:192.168.110.31, vgroup:test_save_order_group
18:07:53.837 INFO --- [verHandlerThread 1 49 500] i.seata.server.coordinator.AbstractCore: Register branch successfully, xid = 192.168.110.31:8092:6800691518082474159, branchId = 6800691518082474172, resourceId = jdbc:mysql://127.0.0.1:3306/test_distribution_cart_db, lockKeys = tb_cart_item:10
18:07:54.948 INFO --- [batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler: SeataMergeMessage xid=192.168.110.31:8092:6800691518082474159, extraData=null, clientIp:192. 168.110.31, vgroup:test_save_order_group

# 4.删除购物项的分支事务回滚成功
18:07:56.867 INFO --- [verHandlerThread_1_50_500] io.seata.server.
coordinator.DefaultCore: Rollback branch transaction successfully, xid =
192.168.110.31:8092:6800691518082474159 branchId = 6800691518082474172
# 5.扣减库存分支事务回滚成功

18:07:58.235 INFO --- [verHandlerThread_1_50_500]
io.seata.server.coordinator.DefaultCore: Rollback branch transaction
successfully, xid = 192.168.110.31:8092:6800691518082474159 branchId =
6800691518082474168
# 6.全局事务回滚成功

18:07:58.785 INFO --- [verHandlerThread_1_50_500] io.seata.server.
coordinator.DefaultCore: Rollback global transaction successfully, xid =
192.168.110.31:8092:6800691518082474159.

在日志中可以看到,saveOrder() 方法执行时会开启一个全局事务。在调用另外两个服务实例时会依次开启两个分支事务。由于 saveOrder() 方法中出现了异常,因此导致了全局事务的回滚操作。

接下来把那一行导致异常的代码删除,看一看一切正常的情况下 Seata 组件会做哪些操作。修改代码后重启订单服务并确认该服务与 Nacos ServerSeata Server 通信成功,再次请求如下测试地址:

http://localhost:8167/order/saveOrder?cartId=10

此时,没有出现异常信息,该接口返回的结果是 “true”。依次查看三个数据库中的表数据,订单表中新增了一条订单数据,id 为 10 的订单项数据被删除,id 为 2035 的商品库存也扣减成功。分别查看微服务实例的控制台输出日志,内容如下。

商品服务的控制台输出日志:

2023-05-26 18:39:05.685 INFO 31246 --- [ch_RMROLE_1_4_8]
i.s.c.r.p.c.RmBranchCommitProcessor: rm client handle branch commit
process:xid=192.168.110.31:8092:6800691518082474238,branchId=68006915180
82474245,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/test_distr
ibution_cart_db,applicationData=null
2023-05-26 18:39:05.708 INFO 31246 --- [ch_RMROLE_1_4_8]
io.seata.rm.AbstractRMHandler: Branch committing:
192.168.110.31:8092:6800691518082474238 6800691518082474245
jdbc:mysql://127.0.0.1:3306/test_distribution_cart_db null
2023-05-26 18:39:05.719 INFO 31246 --- [ch_RMROLE_1_4_8]
io.seata.rm.AbstractRMHandler: Branch commit result: PhaseTwo Committed

商品服务的控制台输出日志:

2023-05-26 18:39:05.685 INFO 31246 --- [ch_RMROLE_1_4_8]
i.s.c.r.p.c.RmBranchCommitProcessor: rm client handle branch commit
process:xid=192.168.110.31:8092:6800691518082474238,branchId=68006915180
82474245,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/test_distr
ibution_cart_db,applicationData=null
2023-05-26 18:39:05.708 INFO 31246 --- [ch_RMROLE_1_4_8]
io.seata.rm.AbstractRMHandler: Branch committing:
192.168.110.31:8092:6800691518082474238 6800691518082474245
jdbc:mysql://127.0.0.1:3306/test_distribution_cart_db null
2023-05-26 18:39:05.719 INFO 31246 --- [ch_RMROLE_1_4_8]
io.seata.rm.AbstractRMHandler: Branch commit result: PhaseTwo_Committed

这两个微服务实例都在 Seata 组件的协调下完成了如下的关键步骤。

  1. rm client-handle branch commit process:RM 执行提交流程。

  2. Branch committing:分支事务正在提交。

  3. PhaseTwo_Committed:分支事务提交完成。

全局事务执行成功,所有的分支事务都执行了提交操作,数据库中的数据也就对应地被修改了。

查看 Seata Server 可以看到更为详细的日志信息,内容如下(笔者在日志中添加了一些中文注释):

# 1.开启全局事务 xid:192.168.110.31:8092:6800691518082474238
18:38:57.806 INFO --- [batchLoggerPrint_1_1]
i.s.c.r.p.server.BatchLogHandler: SeataMergeMessage
timeout=60000,transactionName=saveOrder(int),clientIp:192.168.110.31,
vgroup:test_save_order_group
18:38:57.927 INFO --- [verHandlerThread_1_19_500]
i.s.s.coordinator.DefaultCoordinator: Begin new global transaction
applicationId: order-server,transactionServiceGroup: test_save_order_group,
transactionName:
saveOrder(int),timeout:60000,xid:192.168.110.31:8092:6800691518082474238

# 2.开启扣减库存的分支事务 branchId = 6800691518082474241
18:38:58.748 INFO --- [batchLoggerPrint_1_1]
i.s.c.r.p.server.BatchLogHandler: SeataMergeMessage
xid=192.168.110.31:8092:6800691518082474238,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/test_distribution_goods_db,lockKey=tb_goods:2025,clientIp:192.168.110.31,vgroup:test_save_order_group
18:38:59.710 INFO --- [verHandlerThread_1_20_500]
i.seata.server.coordinator.AbstractCore : Register branch successfully,
xid = 192.168.110.31:8092:6800691518082474238, branchId =
6800691518082474241, resourceId =
jdbc:mysql://127.0.0.1:3306/test_distribution_goods_db,lockKeys =
tb_goods:2025

# 3.开始删除购物项的分支事务 branchId = 6800691518082474245

18:39:00.655 INFO --- [batchLoggerPrint_1_1]
i.s.c.r.p.server.BatchLogHandler : SeataMergeMessage
xid=192.168.110.31:8092:6800691518082474238,branchType=AT,resourceId=jdbc
:mysql://127.0.0.1:3306/test_distribution_cart_db,lockKey=tb_cart_item:
10,clientIp:192.168.110.31,vgroup:test_save_order_group
18:39:01.604 INFO --- [verHandlerThread_1_21_500]
i.seata.server.coordinator.AbstractCore : Register branch successfully,
xid = 192.168.110.31:8092:6800691518082474238, branchId =
6800691518082474245, resourceId =
jdbc:mysql://127.0.0.1:3306/test_distribution_cart_db,lockKeys =
tb_cart_item:10

# 4.开始新增订单的分支事务 branchId = 6800691518082474249

18:39:02.437 INFO --- [batchLoggerPrint_1_1]
i.s.c.r.p.server.BatchLogHandler : SeataMergeMessage
xid=192.168.110.31:8092:6800691518082474238,branchType=AT,resourceId=jdbc
:mysql://127.0.0.1:3306/test_distribution_order_db,lockKey=tb_order:1
,clientIp:192.168.110.31,vgroup:test_save_order_group
18:39:03.542 INFO --- [verHandlerThread_1_22_500]
i.seata.server.coordinator.AbstractCore : Register branch successfully,
xid = 192.168.110.31:8092:6800691518082474238, branchId =
6800691518082474249, resourceId =
jdbc:mysql://127.0.0.1:3306/test_distribution_order_db,lockKeys = tb_order:1

# 5.全局事务提交成功

18:39:03.975 INFO --- [batchLoggerPrint_1_1]
i.s.c.r.p.server.BatchLogHandler : SeataMergeMessage
xid=192.168.110.31:8092:6800691518082474238,extraData=null
,clientIp:192.168.110.31,vgroup:test_save_order_group
18:39:06.263 INFO --- [AsyncCommitting_1_1]
io.seata.server.coordinator.DefaultCore : Committing global transaction is
successfully done, xid = 192.168.110.31:8092:68006915180082474238.

本节演示了分布式事务正常提交和异常回滚的两种情况,使用 Seata AT 模式来处理分布式事务的方案就完整地实现了。