之前我们聊完了分布式框架spring cloud,今天我们来聊下由分布式应用带来的问题,分布式事物。
什么是分布式事物,这里摘录一段分布式事物定义:
提起分布式事务,最早指的涉及多个资源的数据库事务问题。
wiki对分布式事务的定义:
Adistributed transaction is a database transaction in which two or more networkhosts are involved.
不过事务一词含义随着SOA架构逐渐扩大,根据上下文不同,可分为两类:
-
Systemtransaction
2.Businesstransaction
前者多指数据库事务,后者则多对应一个业务交易。与此同时,分布式事务的含义也在泛化,
尤其SOA、微服务概念流行起来后,多指的是一个业务场景,需要编排很多独立部署的服务时,
如何保证交易整体的原子性与一致性问题,这类分布式事务也称作长事务(long-livedtransaction),
例如一个定行程的交易,它由购买航班、租车以及预订酒店构成,而航班预订可能需要一两天才能确认。
为了统一对概念的理解,本文默认指的都是这类长事务。
目前市面上已经有很多解决方案:TCC,SAGA,XA, SEATA等。TCC 方案有
ByteTCC、TCC-transaction、EasyTransaction等,SEATA也支持TCC模式,之所以说seata 是分布式事物一站式解决方案,是因为他集成了TCC,SAGA,XA(规划中)等方案。笔者之前也调研了一部分TCC框架,经过试验和组内反馈,使用是没有问题,但是TCC 方案对业务侵入太大,所谓TCC 也就是 try-confirm-catch ,原本一个动作需要拆分成三个逻辑接口,即资源申请冻结接口,确认操作接口,以及try接口失败的回滚接口。可以看到此方案对业务需要进行业务改造,当然也有优点,对数据库等资源没有使用锁等机制。
那么seata又是如何来解决的呢,我们先来看一张图片以及名词术语结束:
Seata术语
TC - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
从图中我们可以看到 seata 将 事物TC(事物协调者)独立出来部署了。
结合上面的图示,说下seta框架的大致思路:
1.业务层发起远程服务调用时,首先向TC注册全局事务。
2.远程服务调用被调用时,先向TC注册分支事物(分支事物的ACID由本地事物提供保障),并且会向tc汇报分支事物的状态。
3.当所远程服务执行成功后会提交本地事务。
4,远程服务执行失败或者超时等,TC 会向所有RM 发起本地事物回滚指令,进行全局回滚。(如何回滚,当提交本地事物的时候,seata 会生成本地回滚sql 的镜像sql 存储至undo_log 表)
以上是seata 的大致流程思路,详细流程可以参考官方文档:
http://seata.io/zh-cn/docs/overview/what-is-seata.html
下面我们开始搭建seata 环境:
首先搭建seata server;下载地址:
http://seata.io/zh-cn/blog/download.html
当然也可以从git 自行下载最新版本:https://github.com/seata/seata
如果是从git拉取代码,启动server 之前需要先执行命令:
mvn clean install -DskipTests=true -U
打包命令:mvn clean install -DskipTests=true -U -P release-seata
如果是下载源码启动服务,只需要找到seata-server 目录下的Server 类右击run就可以了。如图
默认配置是file模式。即所有的全局事物,所信息等都是本次存储。
当然也可以修改file.conf 配置,修改为数据库存储(目前seata 支持 mysql 和oracle)
默认配置file 模式启动看到如下日志表示seata server 启动成功:
另外如果是需要以db形式存储,需要现在数据库执行相关脚本:
脚本在seata server resource 下:
默认是mysql版本的脚本,其他数据库可自行修改。
下面我们来运行官方提供的demo,官方提供了很多版本的demo,demo地址:https://github.com/seata/seata-samples
我们下载demo后,找到dubbo版本的demo:
具体运行什么demo,小伙伴们可以自行根据自己的兴趣选择。
需要使用seata ,需要添加maven依赖:
<seata.version>0.9.0</seata.version> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version>
</dependency>
当然此处有一个小坑,seata 对jackson 的最低版本是2.9.9。
我们依次启动相应的dubbo 服务:
找到上图的dubbo 服务启动入口然后启动;
如果启动服务时碰到如下异常:
请关闭qos启动,这是一个dubbo 在线运维服务,详细参考:
https://blog.csdn.net/u012988901/article/details/84503672
修改配置即可:
官方实例也就是常见的分布式电商系统交易场景,当然实际场景比这个复杂很多,如图:
业务层进行下单请求,分别会调用库存服务进行库存减少,调用订单服务进行下单,订单服务会调用账户服务,进行金额扣减操作。
如果你直接跑这个demo,需要自行准备dubbo的注册中心,以及运行demo
的sql脚本:
dubbo_biz.sql 是业务表,undo_log.sql 是业务库回滚日志表,不同的业务库都需要建立 undo_log表,如果所有服务使用同一个数据库,只需要建立一个就行。
另外业务系统的数据源必须被seata 代理DataSourceProxy(datasource);
这里要敲黑本,划重点。
下面我们来看下代码:
先看业务才能调用:
继续跟踪代码:
这里分别调用了库存服务和订单服务,并且我们看到了,方法上加了一个注解:
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
这个注解的作用是开启seata 全局事物,在我们使用seata 的过程当中,我们唯一需要使用的也就是这一个注解。他就能帮我们搞定所有的分布式事物问题。
这里有小伙伴可能会有疑问,远程服务是如何感知全局事务,注册分支事物的呢,以dubbo 为例,当我们使用@GlobalTransactional 开启全局事物时,调用dubbo服务,seata会在dubbo框架层面传参数XID(透明传参,开发者无需关心),当服务被调用,框架感知到XID 时会进行本地分支事物的注册以及状态汇报等,这些框架层面会帮我们处理掉,这样就可以说是对业务实现零侵入。
这里不再列出demo运行实例,感兴趣的小伙伴可以自行动手运行官方demo。
注意事项:
1,seata 目前最新版本0.9.0 (2019-11,预计12月份发布1.0.0版本),数据库oracle 字段不支持 timestamp, 序列化不支持
2,表结构必须有主键,索引
3,每个业务库都必须建立 undo_log 表。
4,数据源必须被seata代理: new DataSourceProxy(datasource);
目前seata 最新版本为 0.9.0.1, 1.0Release 版本预计十二月份就会发布,期待ing。。。。
最后附上seata 官方文档地址:http://seata.io/zh-cn/docs/overview/what-is-seata.html
以及官方讨论群:(钉钉群),感兴趣的小伙伴可以添加。
本文对seata讲解到此结束,如有不对之处,欢迎指正交流!