引:2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案。Fescar 的愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。为了打造更中立、更开放、生态更加丰富的分布式事务开源社区,经过社区核心成员的投票,大家决定对 Fescar 进行品牌升级,并更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
前置
还是那句话:
源码,是原理的具象化
原理,是代码的抽象化
所以在开始看源码前一定要好好了解Seata的使用方式以及原理,喜欢Seata的wiki。
依赖工具
- Git
- Maven
- JDK1.8
- IntelliJ IDEA
- Mysql
源码拉取
好像是在fescar升级到seata的时候,项目将samples抽出成一个独立的项目了,所以我们需要拉两个工程的代码:
1 | git clone https://github.com/seata/seata.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
- 创建数据库:fescar_demo
- 执行seata-samples/dubbo/src/main/resources/sql/dubbo_biz.sql
- 执行seata-samples/dubbo/src/main/resources/sql/undo_log.sql
启动基础服务
DubboAccountServiceStarter
找到
io.seata.samples.dubbo.starter.DubboAccountServiceStarter
,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦,同时在数据库account_tbl
表中插入了一条记录:1
22019-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
DubboStorageServiceStarter
找到
io.seata.samples.dubbo.starter.DubboStorageServiceStarter
,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦,同时在数据库storage_tbl
表中插入了一条记录:1
22019-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
DubboOrderServiceStarter
找到
io.seata.samples.dubbo.DubboOrderServiceStarter
,直接点击运行main方法,在当前线程下,我们没有看到日志,但是在Server端,我们可以看到下面的日志,就代码Rm已经注册成功啦:1
22019-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 | public void purchase(String userId, String commodityCode, int orderCount) { |
然后点击运行io.seata.samples.dubbo.DubboBusinessTester#main
方法,我们看到方法正常结束,并且表中数据发送了如下变化:
异常回滚
我们先把io.seata.samples.dubbo.service.impl#purchase
方法中的异常的注释给放开, 为了更好的看到回滚的过程,我们在抛出异常的那一行代码前面加上断点,然后debug运行io.seata.samples.dubbo.DubboBusinessTester#main
方法,执行到断点,我们看看数据库的变化:
我们看到数据其实都已经有了一些变化,然后看看很重要的 undo_log
表,发现它也生成了一些记录,不过这里先不管:
我们放开断点之后看到方法执行抛出了异常,同时表中数据变成了上面正常提交的数据,同时undo_log
表也被清空了,从这里可以看出seata确实是帮我们实现了分布式事务。
结束
好了,环境到这里就搭建结束了,关于seata代码上是如何实现分布式事务的,我们慢慢来分析~