Seata源码解析——环境搭建

引:2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案。Fescar 的愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。为了打造更中立、更开放、生态更加丰富的分布式事务开源社区,经过社区核心成员的投票,大家决定对 Fescar 进行品牌升级,并更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。

前置

还是那句话:
源码,是原理的具象化
原理,是代码的抽象化

所以在开始看源码前一定要好好了解Seata的使用方式以及原理,喜欢Seata的wiki

依赖工具

  1. Git
  2. Maven
  3. JDK1.8
  4. IntelliJ IDEA
  5. Mysql

源码拉取

好像是在fescar升级到seata的时候,项目将samples抽出成一个独立的项目了,所以我们需要拉两个工程的代码:

  1. seata
  2. seata-samples
1
2
git clone https://github.com/seata/seata.git
git clone https://github.com/seata/seata-samples.git

环境搭建

启动Seata Server

在seata工程中找到io.seata.server.Server,直接点击运行main方法即可,我们可以看到如下输出日志:

1
2019-06-16 09:11:11.542 INFO [main]io.seata.core.rpc.netty.AbstractRpcRemotingServer.start:176 -Server started ...

启动Seata-samples

seata-samples项目中提供了各种Demo,我们这里选择最简单的一种好了——dubbo。

初始化数据库

使用mysql

  1. 创建数据库:fescar_demo
  2. 执行seata-samples/dubbo/src/main/resources/sql/dubbo_biz.sql
  3. 执行seata-samples/dubbo/src/main/resources/sql/undo_log.sql

启动基础服务

  1. DubboAccountServiceStarter

    找到io.seata.samples.dubbo.starter.DubboAccountServiceStarter,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦,同时在数据库account_tbl表中插入了一条记录:

    1
    2
    2019-06-16 09:32:45.562 INFO [ServerHandlerThread_1_500]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegRmMessage:112 -rm register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://127.0.0.1:3306/seata', applicationId='dubbo-demo-account-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0xcbff48be, L:/127.0.0.1:8091 - R:/127.0.0.1:54122]
    2019-06-16 09:32:48.139 INFO [NettyServerNIOWorker_2_8]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegTmMessage:128 -checkAuth for client:127.0.0.1:54123 vgroup:my_test_tx_group ok

acount.png

  1. DubboStorageServiceStarter

    找到io.seata.samples.dubbo.starter.DubboStorageServiceStarter,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦,同时在数据库storage_tbl表中插入了一条记录:

    1
    2
    2019-06-16 09:41:06.227 INFO [ServerHandlerThread_2_500]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegRmMessage:112 -rm register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://127.0.0.1:3306/seata', applicationId='dubbo-demo-storage-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0xf37dc389, L:/127.0.0.1:8091 - R:/127.0.0.1:54214]
    2019-06-16 09:41:08.969 INFO [NettyServerNIOWorker_4_8]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegTmMessage:128 -checkAuth for client:127.0.0.1:54215 vgroup:my_test_tx_group ok

storage.png

  1. DubboOrderServiceStarter

    找到io.seata.samples.dubbo.DubboOrderServiceStarter,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦:

    1
    2
    2019-06-16 09:44:18.384 INFO [ServerHandlerThread_3_500]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegRmMessage:112 -rm register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://127.0.0.1.200:3306/seata', applicationId='dubbo-demo-order-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0xe370a8ab, L:/127.0.0.1:8091 - R:/127.0.0.1:54259]
    2019-06-16 09:44:21.333 INFO [NettyServerNIOWorker_6_8]io.seata.core.rpc.DefaultServerMessageListenerImpl.onRegTmMessage:128 -checkAuth for client:127.0.0.1:54261 vgroup:my_test_tx_group ok

测试分布式事务

找到io.seata.samples.dubbo.DubboBusinessTester 这里执行的是一个购买的方法,会先调用库存服务扣减库存,然后调用订单服务生产订单,在生成订单之前,他还会调用扣减账户余额的服务,这样会形成一个微服务链路。

正常提交

我们先把io.seata.samples.dubbo.service.impl#purchase方法中的异常给注释掉:

1
2
3
4
5
6
7
public void purchase(String userId, String commodityCode, int orderCount) {
LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
storageService.deduct(commodityCode, orderCount);
orderService.create(userId, commodityCode, orderCount);
// throw new RuntimeException("xxx");

}

然后点击运行io.seata.samples.dubbo.DubboBusinessTester#main方法,我们看到方法正常结束,并且表中数据发送了如下变化:
normal-acount.png

normal-storage.png

normal-order.png

异常回滚

我们先把io.seata.samples.dubbo.service.impl#purchase方法中的异常的注释给放开, 为了更好的看到回滚的过程,我们在抛出异常的那一行代码前面加上断点,然后debug运行io.seata.samples.dubbo.DubboBusinessTester#main方法,执行到断点,我们看看数据库的变化:
exception-account.png

exception-storage.png

exception-order.png

我们看到数据其实都已经有了一些变化,然后看看很重要的 undo_log表,发现它也生成了一些记录,不过这里先不管:
undo_log.png

我们放开断点之后看到方法执行抛出了异常,同时表中数据变成了上面正常提交的数据,同时undo_log表也被清空了,从这里可以看出seata确实是帮我们实现了分布式事务

结束

好了,环境到这里就搭建结束了,关于seata代码上是如何实现分布式事务的,我们慢慢来分析~

参考

  1. Seata Wiki