Seata 简介

简介: Seata 的前身 Fescar 刚开源的时候,就看过相关的文章和代码,代码写得很好,我还在另一个自己的项目中,借鉴了它的很多设计风格。最近想总结一篇关于分布式事务的文章,所以就想以 Seata 为中心,围绕它来细述分布式事务的点点滴滴。本文作为该系列文章的开篇,先简单地介绍一下 Seata 的背景和使用方式。

引言

Seata 的前身 Fescar 刚开源的时候,就看过相关的文章和代码,代码写得很好,我还在另一个自己的项目中,借鉴了它的很多设计风格。最近想总结一篇关于分布式事务的文章,所以就想以 Seata 为中心,围绕它来细述分布式事务的点点滴滴。本文作为该系列文章的开篇,先简单地介绍一下 Seata 的背景和使用方式,其他 Seata 相关文章均收录于 <Seata系列文章>中。

背景

互联网系统最初的设计一般都是单库单表,但随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。所以,在这个阶段一般都会对数据库进行水平拆分,将原单库单表拆分成多个数据库分片。

如下图所示,分库分表之后,原来在一个数据库上就能完成的写操作,可能就会跨多个数据库,这就产生了跨数据库事务问题。
hor-db
此外,在系统设计初期,一般都是将所有业务放在一个服务中,但是随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,这种单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。
vir-db
如上图所示,本着面向服务(SOA)的设计原则,会将单系统拆分成多个业务系统,这降低了各个系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于各个子业务的发展和子系统容量的伸缩。但是,业务系统按照服务拆分之后,一个完整的业务往往需要调用多个服务,如何保证多个服务间的数据一致性成为一个难题。

为了应对这种需求,在数据库领域发展出了一个 XA 协议,在数据库层面基于 2PC 来实现分布式事务,但是并不是所有的数据库都支持该协议,如果有任意一个子系统使用的数据库不支持 XA 协议的话,就无法保证整个业务流程的一致性。此外,XA 的执行效率也很差,而且因为它处于数据库实现领域,所以数据库的使用者对此毫无办法,当然您也可以直接定制化数据库实现,但是成本很大。

为了解决上述问题,专注于分布式事务的应用层中间件就应运而生,它们有的是 2PC 型无侵入方案,有的是 TCC 方案。而我们今天介绍的主角 Seata,将各种方案都整合到了一起,并针对性能进行了很多优化,可谓是集大成者。接下来就让我们一起看一看 Seata 带给我们的便利,以及 Seata 的实现原理。

试玩

前面提到了 Seata 会给开发带来极大的便利,这里就以官方Demo为例,先给大家演示一下如何使用 Seata。这里我使用了官方 Demo 中的 springboot-dubbo-seata,因为它涉及到前面提到的多服务场景,每个服务操作各自的 DB(名义上),使用 dubbo 进行 RPC,从而完成整个业务流程。

我的试玩流程:

  1. 下载代码
  2. 导入 IntelliJ IDEA,并下载 maven 依赖
  3. 下载 1.1.0 版本的 nacos,后面会用它进行 RPC 的服务发现
  4. 下载 0.8.0 版本的 seata-server,这里它扮演分布式事务协调者
  5. 在本地的 MySQL 服务中创建 seata 数据库,并将 springboot-dubbo-seata 中的样例 SQL 导入到 seata 数据库,完成后可以发现数据库中有 t_account,t_order,t_storage,undo_log 这四个表,并且 t_account 中有一条用户数据,t_storage 中有一条商品信息。
  6. 然后分别启动 3 个子服务 samples-accountsamples-ordersamples-storage,最后启动业务入口服务 samples-business
  7. 通过 curl 调用业务入口服务
⚠️注意:我当时测试时,官方 Guide 中使用的 curl 命令有一些问题,其中使用的商品码有误,这里需要将其改为 DB 中保存的商品码 C201901140001,我已经给官方仓库提了 PR,但是可能还未合并。总之,最终完整的 curl 命令如下:
   curl -H "Content-Type:application/json" -X POST -d '{"userId":"1","commodityCode":"C201901140001","name":"风扇","count":2,"amount":"100"}' localhost:8104/business/dubbo/buy

事务提交

sample-success.png
当业务流程成功完成时,请求结果是成功。查看 DB 你会发现 t_account 中用户的余额减少了:4000 -> 3900,t_storage 中商品的数量减少了: 1000 -> 998,t_order 中新增了一条订单。
new-order

事务回滚

接下来我们测试一下事务回滚的效果,首先打开回滚异常。
rollback-check
然后重启入口服务 samples-business,并重新发送请求,请求结果变成了失败,此时查看 DB 你会发现它没有任何变化。
sample-failure
我们回过头来看看业务入口的代码,你会发现这一切特性的接入对开发者来说,只是将全局事务注解 GlobalTransactional 标在入口函数上,然后在所有服务中引入 Seata,并进行正确的配置,剩下的脏活累活 Seata 就自动帮我们完成了, 是不是感觉很神奇?

/**
 * 处理业务逻辑
 */
@Override
// 全局事务注解
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")
public ObjectResponse handleBusiness(BusinessDTO businessDTO) {
    System.out.println("开始全局事务,XID = " + RootContext.getXID());
    ObjectResponse<Object> objectResponse = new ObjectResponse<>();
    //1、扣减库存
    CommodityDTO commodityDTO = new CommodityDTO();
    commodityDTO.setCommodityCode(businessDTO.getCommodityCode());
    commodityDTO.setCount(businessDTO.getCount());
    ObjectResponse storageResponse = storageDubboService.decreaseStorage(commodityDTO);
    //2、创建订单
    OrderDTO orderDTO = new OrderDTO();
    orderDTO.setUserId(businessDTO.getUserId());
    orderDTO.setCommodityCode(businessDTO.getCommodityCode());
    orderDTO.setOrderCount(businessDTO.getCount());
    orderDTO.setOrderAmount(businessDTO.getAmount());
    ObjectResponse<OrderDTO> response = orderDubboService.createOrder(orderDTO);

    //打开注释测试事务发生异常后,全局回滚功能
    if (!flag) {
        throw new RuntimeException("测试抛异常后,分布式事务回滚!");
    }

    if (storageResponse.getStatus() != 200 || response.getStatus() != 200) {
        throw new DefaultException(RspStatusEnum.FAIL);
    }

    objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode());
    objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage());
    objectResponse.setData(response.getData());
    return objectResponse;
}

从业务服务的实现代码中可以看到,我们先调用了库存服务减少库存,然后调用订单服务创建订单,在订单服务里面,涉及到了减少用户余额(代码就不展示了),当所有的这一切都完成时,业务流程中抛出一个运行时异常,致使整个业务链路中的所有 DB 事务都回滚了。

至此,系统垂直扩展引入的分布式事务问题就解决了。
vir-db2
好了,实验课结束了,该学一下理论知识了。

文章说明

更多有价值的文章均收录于贝贝猫的文章目录

stun

版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

创作声明: 本文基于下列所有参考内容进行创作,其中可能涉及复制、修改或者转换,图片均来自网络,如有侵权请联系我,我会第一时间进行删除。

参考内容

[1] fescar锁设计和隔离级别的理解
[2] 分布式事务中间件 Fescar - RM 模块源码解读
[3] Fescar分布式事务实现原理解析探秘
[4] Seata TCC 分布式事务源码分析
[5] 深度剖析一站式分布式事务方案 Seata-Server
[6] 分布式事务 Seata Saga 模式首秀以及三种模式详解
[7] 蚂蚁金服大规模分布式事务实践和开源详解
[8] 分布式事务 Seata TCC 模式深度解析
[9] Fescar (Seata)0.4.0 中文文档教程
[10] Seata Github Wiki
[11] 深度剖析一站式分布式事务方案Seata(Fescar)-Server

相关文章
|
2月前
|
SQL NoSQL 数据库
SpringCloud基础6——分布式事务,Seata
分布式事务、ACID原则、CAP定理、Seata、Seata的四种分布式方案:XA、AT、TCC、SAGA模式
SpringCloud基础6——分布式事务,Seata
|
NoSQL Java Redis
【Seata】分布式事务框架Seata踩坑集锦
【Seata】分布式事务框架Seata踩坑集锦
【Seata】分布式事务框架Seata踩坑集锦
|
6月前
|
SQL 容灾 数据库
Seata笔记2
Seata笔记2
37 1
|
容灾 Shell Nacos
【Seata】seata的部署和集成
一、部署Seata的tc-server 1.下载 首先我们要下载seata-server包,地址在http://seata.io/zh-cn/blog/download.html
232 0
|
6月前
|
关系型数据库 MySQL Apache
|
6月前
|
SQL 关系型数据库 数据库
seata开发指南
seata开发指南
|
6月前
|
SQL FESCAR 数据库
SpringCloud之Seata基本介绍与安装
SpringCloud之Seata基本介绍与安装
|
SQL 缓存 搜索推荐
分布式事务简介(seata)
分布式事务简介(seata)
208 0
|
Java 中间件 uml
阿里中间件seata源码剖析三:聊聊seata中的ShutdownHook
阿里中间件seata源码剖析三:聊聊seata中的ShutdownHook
275 7
阿里中间件seata源码剖析三:聊聊seata中的ShutdownHook
|
存储 SQL SpringCloudAlibaba
十一.SpringCloudAlibaba极简入门-分布式事务实战seata
在单体应用中通常情况下只有一个数据库(单数据源),集成事务是一个非常容易的工作。Spring对事务做了很好的管理,我们只需要通过简单的注解@Transactional就可以完成本地事务管理。 但是在微服务项目中事务的管理变得困难,因为微服务项目往往有很多的数据库组成,如果在一个业务中涉及到了对多个微服务以及多个数据库的写操作(跨多个数据源),那么要如何才能保证多个数据库组件的读写一致呢?即:同时操作两个数据库,数据库A写操作成功过,数据库B写操作失败要怎么样让数据库A的写操作回滚?很显然用本地事务管理是不能实现了。 我们知道,虽然Spring对事务做了很好的管理和封装,但是最终都是调用数据
下一篇
无影云桌面