精华内容
下载资源
问答
  • seata

    2021-03-25 23:35:53
    seata AT模式 & 多数据源 下载seata 软件包 安装前提:安装了JDK 下载地址:https://github.com/seata/seata/releases 我这里下载的是最新版seata-server-1.4.1.tar.gz 初始化 Seata TC Server 的 db 数据库 ...

    nacos & seata AT模式 & 多数据源

    下载seata 软件包
    安装前提:安装了JDK
    
    下载地址:https://github.com/seata/seata/releases
    我这里下载的是最新版seata-server-1.4.1.tar.gz
    
    初始化 Seata TC Server 的 db 数据库
    DROP DATABASE IF EXISTS seata;
    CREATE DATABASE seata DEFAULT CHARACTER SET utf8;
    
    DROP TABLE IF EXISTS seata.global_table;
    CREATE TABLE seata.global_table
    (
        `xid`                       VARCHAR(128) NOT NULL,
        `transaction_id`            BIGINT,
        `status`                    TINYINT      NOT NULL,
        `application_id`            VARCHAR(32),
        `transaction_service_group` VARCHAR(32),
        `transaction_name`          VARCHAR(128),
        `timeout`                   INT,
        `begin_time`                BIGINT,
        `application_data`          VARCHAR(2000),
        `gmt_create`                DATETIME,
        `gmt_modified`              DATETIME,
        PRIMARY KEY (`xid`),
        KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
        KEY `idx_transaction_id` (`transaction_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
    
    DROP TABLE IF EXISTS seata.branch_table;
    CREATE TABLE seata.branch_table
    (
        `branch_id`         BIGINT       NOT NULL,
        `xid`               VARCHAR(128) NOT NULL,
        `transaction_id`    BIGINT,
        `resource_group_id` VARCHAR(32),
        `resource_id`       VARCHAR(256),
        `branch_type`       VARCHAR(8),
        `status`            TINYINT,
        `client_id`         VARCHAR(64),
        `application_data`  VARCHAR(2000),
        `gmt_create`        DATETIME(6),
        `gmt_modified`      DATETIME(6),
        PRIMARY KEY (`branch_id`),
        KEY `idx_xid` (`xid`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
    
    DROP TABLE IF EXISTS seata.lock_table;
    CREATE TABLE seata.lock_table
    (
        `row_key`        VARCHAR(128) NOT NULL,
        `xid`            VARCHAR(96),
        `transaction_id` BIGINT,
        `branch_id`      BIGINT       NOT NULL,
        `resource_id`    VARCHAR(256),
        `table_name`     VARCHAR(32),
        `pk`             VARCHAR(36),
        `gmt_create`     DATETIME,
        `gmt_modified`   DATETIME,
        PRIMARY KEY (`row_key`),
        KEY `idx_branch_id` (`branch_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
    
    修改 conf/file 配置文件

    修改使用 db 数据库,实现 Seata TC Server 的全局事务会话信息的共享

    service {
      vgroupMapping.multi-datasource-service-group = "default"
    }
    ## transaction log store, only used in seata-server
    store {
      ## store mode: file、db、redis
      mode = "db"
    
      ## database store property
      db {
        ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
        datasource = "dbcp"
        ## mysql/oracle/postgresql/h2/oceanbase etc.
        dbType = "mysql"
        driverClassName = "com.mysql.cj.jdbc.Driver"
        url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false"
        user = "root"
        password = "123456"
        minConn = 5
        maxConn = 100
        globalTable = "global_table"
        branchTable = "branch_table"
        lockTable = "lock_table"
        queryLimit = 100
        maxWait = 5000
      }
    }
    
    修改 conf/registry.conf 配置文件

    设置使用 Nacos 注册中心

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "nacos"
      loadBalance = "RandomLoadBalance"
      loadBalanceVirtualNodes = 10
    
      nacos {
        application = "seata-server"
        serverAddr = "192.168.199.112:8848"
        group = "seata_demo"
        namespace = "d197f572-96bf-400f-b21e-0f38a2198ef1"
        cluster = "default"
        username = "nacos"
        password = "nacos"
      }
      file {
        name = "file.conf"
      }
    }
    
    config {
      # file、nacos 、apollo、zk、consul、etcd3
      type = "nacos"
    
      nacos {
        serverAddr = "192.168.199.112:8848"
        namespace = "d197f572-96bf-400f-b21e-0f38a2198ef1"
        group = "seata_demo"
        username = "nacos"
        password = "nacos"
      }
      
      file {
        name = "file.conf"
      }
    }
    
    
    导入配置信息到 Nacos 配置中心

    Seata 官方提供了将配置信息(file.conf)批量导入到各种主流配置中心的 Shell 脚本,存放路径是在 Seata源码目录 script/config-center 目录

    其中 config.txt 为通用参数文件,包含了 Seata Server(TC)需要的所有配置信息,需要根据实际情况更改文件里的以下内容
    
    service.vgroupMapping.multi-datasource-service-group = "default"
    
    store.mode=db
    store.db.datasource=druid
    store.db.dbType=mysql
    store.db.driverClassName=com.mysql.cj.jdbc.Driver
    store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    store.db.user=root
    store.db.password=123456
    
    sh nacos-config.sh -h 127.0.0.1 -p 8848 -t d197f572-96bf-400f-b21e-0f38a2198ef1 -g seata_demo
    

    在这里插入图片描述

    启动 TC Server
    启动第一个 TC Server 在后台
    nohup sh bin/seata-server.sh -p 18091 -n 1 &
    
    启动第二个 TC Server 在后台
    nohup sh bin/seata-server.sh -p 28091 -n 2 &
    

    在这里插入图片描述

    运行项目

    代码地址

    初始化数据库

    使用代码地址中,data.sql 脚本,创建 seata_order、seata_storage、seata_amount 三个库

    然后启动项目,确保启动过程中没有任何报错信息,我遇到了如下错误,应该都是之前配置没对,得仔细检查

    can not get cluster name in registry config 'service.vgroupMapping.multi-datasource-service-group', please make sure registry config correct
    
    no available service 'null' found, please make sure registry config correct
    

    在这里插入图片描述

    简单测试
    分布式事务正常提交

    目前数据库的数据情况

    mysql> select * from seata_order.orders;
    Empty set (0.00 sec)
    
    mysql> select * from seata_product.product;
    +----+-------+---------------------+
    | id | stock | last_update_time    |
    +----+-------+---------------------+
    |  1 |    10 | 2021-03-25 18:28:49 |
    +----+-------+---------------------+
    1 row in set (0.00 sec)
    
    mysql> select * from seata_account.account;
    +----+---------+---------------------+
    | id | balance | last_update_time    |
    +----+---------+---------------------+
    |  1 |      10 | 2021-03-25 18:28:49 |
    +----+---------+---------------------+
    1 row in set (0.00 sec)
    
    

    创建订单接口

    @Override
    @DS(value = "order-ds")
    @GlobalTransactional
    public Integer createOrder(Long userId, Long productId, Integer price) throws Exception {
        Integer amount = 1; // 购买数量,暂时设置为 1。
    
        logger.info("[createOrder] 当前 XID: {}", RootContext.getXID());
    
        // 扣减库存
        productService.reduceStock(productId, amount);
    
        // 扣减余额
        accountService.reduceBalance(userId, price);
    
        // 保存订单
        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);
        orderDao.saveOrder(order);
        logger.info("[createOrder] 保存订单: {}", order.getId());
    
        // 返回订单编号
        return order.getId();
    }
    

    使用 Postman 模拟调用 http://127.0.0.1:8081/order/create 创建订单的接口
    在这里插入图片描述

    此时控制台日志,显示如下
    在这里插入图片描述

    再查询下目前数据库的数据情况
    在这里插入图片描述

    分布式事务异常回滚

    使用 Postman 模拟调用 http://127.0.0.1:8081/order/create 创建订单的接口
    在这里插入图片描述

    控制台日志
    在这里插入图片描述

    再查询下目前数据库的数据情况
    在这里插入图片描述

    参考:

    分布式事务,这一篇就够了

    分布式事务 Seata 及其三种模式详解 | Meetup#3 回顾

    Seata 入门教程 - 实战篇(电商)

    Seata 极简入门

    芋道 Spring Boot 分布式事务 Seata 入门

    展开全文
  • Seata

    2021-01-20 18:53:31
    目录 ...三、Seata 及其三种模式详解 3.1 分布式事务 Seata 介绍 3.2 分布式事务 Seata 产品模块 3.3 分布式事务 Seata 解决方案 集成实战 方案对比 概况 Spring Cloud Alibaba Git:https

    目录

    概况

    发展历程

    架构分析

    一、分布式事务产生的背景

    1.1 分布式架构演进之 - 数据库的水平拆分

    1.2 分布式架构演进之 - 业务服务化拆分

    二、分布式事务理论基础

    2.1 两阶段提交协议

    2.2 TCC

    2.3 Saga

    三、Seata 及其三种模式详解

    3.1 分布式事务 Seata 介绍

    3.2 分布式事务 Seata 产品模块

    3.3 分布式事务 Seata 解决方案

    集成实战

    方案对比


    概况

    Spring Cloud Alibaba Git:https://github.com/alibaba/spring-cloud-alibaba

    Git:https://github.com/seata/

    官方文档:http://seata.io/zh-cn/

    Seata(Simple Extensible Autonomous Transaction Architecture) 是 阿里巴巴开源的分布式事务中间件,以高效并且对业务 0 侵入的方式,解决微服务场景下面临的分布式事务问题。

    发展历程

    image

    架构分析

    蚂蚁架构部分享文档:https://juejin.im/post/6844903913691283469

    包含了分布式事务产生的背景,分布式事务理论基础,Seata原理及模式详解。内容如下

    一、分布式事务产生的背景

    1.1 分布式架构演进之 - 数据库的水平拆分

    蚂蚁金服的业务数据库起初是单库单表,但随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。所以我们对数据库进行了水平拆分,将原单库单表拆分成数据库分片。

    如下图所示,分库分表之后,原来在一个数据库上就能完成的写操作,可能就会跨多个数据库,这就产生了跨数据库事务问题。

    image

    1.2 分布式架构演进之 - 业务服务化拆分

    在业务发展初期,“一块大饼”的单业务系统架构,能满足基本的业务需求。但是随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。

    如下图所示,蚂蚁金服按照面向服务架构(SOA)的设计原则,将单业务系统拆分成多个业务系统,降低了各系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于业务的发展和系统容量的伸缩。

    image

    业务系统按照服务拆分之后,一个完整的业务往往需要调用多个服务,如何保证多个服务间的数据一致性成为一个难题。

    二、分布式事务理论基础

    2.1 两阶段提交协议

    image

    两阶段提交协议:事务管理器分两个阶段来协调资源管理器,第一阶段准备资源,也就是预留事务所需的资源,如果每个资源管理器都资源预留成功,则进行第二阶段资源提交,否则协调资源管理器回滚资源。

    2.2 TCC

    image

    TCC(Try-Confirm-Cancel) 实际上是服务化的两阶段提交协议,业务开发者需要实现这三个服务接口,第一阶段服务由业务代码编排来调用 Try 接口进行资源预留,所有参与者的 Try 接口都成功了,事务管理器会提交事务,并调用每个参与者的 Confirm 接口真正提交业务操作,否则调用每个参与者的 Cancel 接口回滚事务。

    2.3 Saga

    image

    Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

    分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

    Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas。

    Saga 正向服务与补偿服务也需要业务开发者实现。

    三、Seata 及其三种模式详解

    3.1 分布式事务 Seata 介绍

    Seata(Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架)是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。Seata 开源半年左右,目前已经有超过 1.1 万 star,社区非常活跃。我们热忱欢迎大家参与到 Seata 社区建设中,一同将 Seata 打造成开源分布式事务标杆产品。

    Seata:github.com/seata/seata

    image

    3.2 分布式事务 Seata 产品模块

    如下图所示,Seata 中有三大模块,分别是 TM、RM 和 TC。 其中 TM 和 RM 是作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署。

    image

    在 Seata 中,分布式事务的执行流程:

    • TM 开启分布式事务(TM 向 TC 注册全局事务记录);
    • 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态);
    • TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
    • TC 汇总事务信息,决定分布式事务是提交还是回滚;
    • TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;

    3.3 分布式事务 Seata 解决方案

    Seata 会有 4 种分布式事务解决方案,分别是 AT 模式、TCC 模式、Saga 模式和 XA 模式。

    image

    3.3.1 AT 模式

    今年 1 月份,Seata 开源了 AT 模式。AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

    image

    AT 模式如何做到对业务的无侵入 :

    • 一阶段:

    在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

    image

    • 二阶段提交:

    二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

    image

    • 二阶段回滚:

    二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

    image

    AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。

    3.3.2 TCC 模式

    2019 年 3 月份,Seata 开源了 TCC 模式,该模式由蚂蚁金服贡献。TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

    image

    TCC 三个方法描述:

    • Try:资源的检测和预留;
    • Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
    • Cancel:预留资源释放;

    蚂蚁金服在 TCC 的实践经验

    image

    1 TCC 设计 - 业务模型分 2 阶段设计:

    用户接入 TCC ,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。

    以“扣钱”场景为例,在接入 TCC 前,对 A 账户的扣钱,只需一条更新账户余额的 SQL 便能完成;但是在接入 TCC 之后,用户就需要考虑如何将原来一步就能完成的扣钱操作,拆成两阶段,实现成三个方法,并且保证一阶段 Try  成功的话 二阶段 Confirm 一定能成功。

    image

    如上图所示,Try 方法作为一阶段准备方法,需要做资源的检查和预留。在扣钱场景下,Try 要做的事情是就是检查账户余额是否充足,预留转账资金,预留的方式就是冻结 A 账户的 转账资金。Try 方法执行之后,账号 A 余额虽然还是 100,但是其中 30 元已经被冻结了,不能被其他事务使用。

    二阶段 Confirm 方法执行真正的扣钱操作。Confirm 会使用 Try 阶段冻结的资金,执行账号扣款。Confirm 方法执行之后,账号 A 在一阶段中冻结的 30 元已经被扣除,账号 A 余额变成 70 元 。

    如果二阶段是回滚的话,就需要在 Cancel 方法内释放一阶段 Try 冻结的 30 元,使账号 A 的回到初始状态,100 元全部可用。

    用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功。相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。

    2 TCC 设计 - 允许空回滚:

    image

    Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器认为已回滚,否则会不断重试,而 Cancel 又没有对应的业务数据可以进行回滚。

    3 TCC 设计 - 防悬挂控制:

    image

    悬挂的意思是:Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致,所以我们在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。

    4 TCC 设计 - 幂等控制:

    image

    幂等性的意思是:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务 xid 或业务主键判重来控制。

    3.3.3 Saga 模式

    Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

    分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

    image

    Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

    1 Saga 模式使用场景

    image

    Saga 模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。

    事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,可以使用 Saga 模式。

    Saga模式的优势是:

    • 一阶段提交本地数据库事务,无锁,高性能;
    • 参与者可以采用事务驱动异步执行,高吞吐;
    • 补偿服务即正向服务的“反向”,易于理解,易于实现;

    缺点:Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。

    2 基于状态机引擎的 Saga 实现

    image

    目前 Saga 的实现一般也两种,一种是通过事件驱动架构实现,一种是基于注解加拦截器拦截业务的正向服务实现。Seata 目前是采用事件驱动的机制来实现的,Seata 实现了一个状态机,可以编排服务的调用流程及正向服务的补偿服务,生成一个 json 文件定义的状态图,状态机引擎驱动到这个图的运行,当发生异常的时候状态机触发回滚,逐个执行补偿服务。当然在什么情况下触发回滚用户是可以自定义决定的。该状态机可以实现服务编排的需求,它支持单项选择、并发、异步、子状态机调用、参数转换、参数映射、服务执行状态判断、异常捕获等功能。

    3 状态机引擎原理

    image

    该状态机引擎的基本原理是,它基于事件驱动架构,每个步骤都是异步执行的,步骤与步骤之间通过事件队列流转, 极大的提高系统吞吐量。每个步骤执行时会记录事务日志,用于出现异常时回滚时使用,事务日志会记录在与业务表所在的数据库内,提高性能。

    4 状态机引擎设计

    image

    该状态机引擎分成了三层架构的设计,最底层是“事件驱动”层,实现了 EventBus 和消费事件的线程池,是一个 Pub-Sub 的架构。第二层是“流程控制器”层,它实现了一个极简的流程引擎框架,它驱动一个“空”的流程执行,“空”的意思是指它不关心流程节点做什么事情,它只执行每个节点的 process 方法,然后执行 route 方法流转到下一个节点。这是一个通用框架,基于这两层,开发者可以实现任何流程引擎。最上层是“状态机引擎”层,它实现了每种状态节点的“行为”及“路由”逻辑代码,提供 API 和状态图仓库,同时还有一些其它组件,比如表达式语言、逻辑计算器、流水生成器、拦截器、配置管理、事务日志记录等。

    5 Saga 服务设计经验

    和TCC类似,Saga的正向服务与反向服务也需求遵循以下设计原则:

    1)Saga 服务设计 - 允许空补偿

    image

    2)Saga 服务设计 - 防悬挂控制

    image

    3)Saga 服务设计 - 幂等控制

    image

    4)Saga 设计 - 自定义事务恢复策略

    image

    前面讲到 Saga 模式不保证事务的隔离性,在极端情况下可能出现脏写。比如在分布式事务未提交的情况下,前一个服务的数据被修改了,而后面的服务发生了异常需要进行回滚,可能由于前面服务的数据被修改后无法进行补偿操作。这时的一种处理办法可以是“重试”继续往前完成这个分布式事务。由于整个业务流程是由状态机编排的,即使是事后恢复也可以继续往前重试。所以用户可以根据业务特点配置该流程的事务处理策略是优先“回滚”还是“重试”,当事务超时的时候,Server 端会根据这个策略不断进行重试。

    由于 Saga 不保证隔离性,所以我们在业务设计的时候需要做到“宁可长款,不可短款”的原则,长款是指在出现差错的时候站在我方的角度钱多了的情况,钱少了则是短款,因为如果长款可以给客户退款,而短款则可能钱追不回来了,也就是说在业务设计的时候,一定是先扣客户帐再入帐,如果因为隔离性问题造成覆盖更新,也不会出现钱少了的情况。

    6 基于注解和拦截器的 Saga 实现

    image

    还有一种 Saga 的实现是基于注解+拦截器的实现,Seata 目前没有实现,可以看上面的伪代码来理解一下,one 方法上定义了 @SagaCompensable 的注解,用于定义 one 方法的补偿方法是 compensateOne 方法。然后在业务流程代码 processA 方法上定义 @SagaTransactional 注解,启动 Saga 分布式事务,通过拦截器拦截每个正向方法当出现异常的时候触发回滚操作,调用正向方法的补偿方法。

    7 两种 Saga 实现优劣对比

    两种 Saga 的实现各有又缺点,下面表格是一个对比:

    image

    状态机引擎的最大优势是可以通过事件驱动的方法异步执行提高系统吞吐,可以实现服务编排需求,在 Saga 模式缺乏隔离性的情况下,可以多一种“向前重试”的事情恢复策略。注解加拦截器的的最大优势是,开发简单、学习成本低。

     

     

    集成实战

     

    方案对比

    事务方案 优点 缺点
    2PC 实现简单 1、需要数据库(一般是XA支持) 2、锁粒度大,性能差
    TCC 锁粒度小,性能好 需要侵入业务,实现较为复杂,复杂业务实现幂等有难度
    消息事务 业务侵入小,无需编写业务回滚补偿逻辑 事务消息实现难度大,强依赖第三方中间件可靠性

    Seata

    整合上述方案优点

    目前还在迭代中,商用有风险

     

    展开全文
  • Spring Cloud Alibaba 06_使用 Seata实现分布式事务 创建 order 模块,选择:spring web、JDBC API 和 MySQL Driver 创建 pay 模块,选择:spring web、JDBC API 和 MySQL Driver 修改 order 和 pay 的父工程: <...

    Spring Cloud Alibaba 06_使用 Seata实现分布式事务

    创建 order 模块,选择:spring webJDBC APIMySQL Driver

    创建 pay 模块,选择:spring webJDBC APIMySQL Driver

    修改 order 和 pay 的父工程:

    <parent>
        <groupId>com.blu</groupId>
        <artifactId>springcloudalibabademo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    

    创建数据库 order,创建表 orders,字段 idusername

    创建数据库 pay,创建表 pay,字段 idusername

    order 模块的 application.yml 配置文件:

    server:
      port: 8090
    
    spring:
      application:
        name: order
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/order
        username: root
        password: 123456
    

    pay 模块的 application.yml 配置文件

    server:
      port: 8100
    
    spring:
      application:
        name: pay
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/pay
        username: root
        password: 123456
    

    OrderService:

    package com.blu.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    
    @Service
    public class OrderService {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void save(){
            this.jdbcTemplate.update("insert into orders(username) value ('张三')");
        }
    
    }
    

    PayService:

    package com.blu.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    
    @Service
    public class PayService {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void save(){
            this.jdbcTemplate.update("insert into pay(username) value ('张三')");
        }
    }
    

    OrderApplication:注册 RestTemplate

    package com.blu;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class OrderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
    }
    

    OrderController:

    package com.blu.controller;
    
    import com.blu.service.OrderService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    public class OrderController {
    
        @Autowired
        private OrderService orderService;
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("save")
        public String save(){
            //创建订单
            this.orderService.save();
            int i = 1/0;
            //支付
            this.restTemplate.getForObject("http://localhost:8100/save",String.class);
            return "success";
        }
    
    }
    

    PayController:

    package com.blu.controller;
    
    import com.blu.service.PayService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class PayController {
    
        @Autowired
        private PayService payService;
    
        @GetMapping("save")
        public String save(){
            this.payService.save();
            return "success";
        }
    
    }
    

    启动访问:http://localhost:8090/save 出现报错

    检查数据库,发现 orders 表成功插入数据,但 pay 表没有,这就是 分布式异常



    使用 Seata 实现分布式事务

    seata-server 下载地址:https://github.com/seata/seata/releases

    解压后,修改 registry.conf:

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "nacos"
    
      nacos {
        serverAddr = "localhost"
        namespace = ""
        cluster = "default"
      }
    
    }
    
    config {
      # file、nacos 、apollo、zk、consul、etcd3
      type = "nacos"
    
      nacos {
        serverAddr = "localhost"
        namespace = ""
      }
    
    }
    

    修改 nacos-config.txt:

    • 删除行:
    service.vgroup_mapping.my_test_tx_group=default
    
    • 添加行:
    service.vgroup_mapping.order=default
    service.vgroup_mapping.pay=default
    
    • 启动 nacos,然后在 seataconf 目录下,执行 nacos-config.sh ,让 seata 的配置写入 nacos 服务中

    注:Windows系统无法直接执行.sh文件,需要使用git bash执行

    sh nacos-config.sh 127.0.0.1
    

    在这里插入图片描述

    • 启动 Seata Servercmd 进入 seatabin 目录,然后使用以下命令启动:

    注:JDK 8 以上的环境无法启动 Seata Server

    seata-server.bat -p 8020 -m file
    
    • 如果在 nacos 的服务列表中出现了 serverAddr 服务,则说明 Seata Server 已经成功启动了

    在这里插入图片描述

    • seataconf 目录下有一个 db_undo_log.sql 文件,打开它,里面有一段创建 undo_log 表的SQL语句:
    CREATE TABLE `undo_log` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `branch_id` bigint(20) NOT NULL,
      `xid` varchar(100) NOT NULL,
      `context` varchar(128) NOT NULL,
      `rollback_info` longblob NOT NULL,
      `log_status` int(11) NOT NULL,
      `log_created` datetime NOT NULL,
      `log_modified` datetime NOT NULL,
      `ext` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    

    分别在 orderpay 数据库中执行以上sql来创建 undo_log 表(用于记录需要回滚的信息)

    • orderpay 两个模块中都添加 SeataNacos Config 组件:
    <dependency>
    	<groupId>com.alibaba.cloud</groupId>
    	<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    	<version>2.1.1.RELEASE</version>
    </dependency>
    <dependency>
    	<groupId>com.alibaba.cloud</groupId>
    	<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    	<version>2.2.1.RELEASE</version>
    </dependency>
    
    • orderpay 两个模块的 JDBCTemplate 添加代理数据源:

    注意这里的 DataSourceProxy 的包是 io.seata.rm.datasource.DataSourceProxy
    DataSource 的包是 javax.sql.DataSource

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
    	return new JdbcTemplate(new DataSourceProxy(dataSource));
    }
    
    • 将之前修改好的 registry.conf 复制到 orderpay 两个模块的 resources 目录下
    • 分别在 orderpay 两个模块中创建 bootstrap.yml 配置文件:
    spring:
      application:
        name: order
      cloud:
        nacos:
          config:
            server-addr: localhost:8848
            namespace: public
            group: SEATA_GROUP
        alibaba:
          seata:
            tx-service-group: ${spring.application.name}
    
    spring:
      application:
        name: pay
      cloud:
        nacos:
          config:
            server-addr: localhost:8848
            namespace: public
            group: SEATA_GROUP
        alibaba:
          seata:
            tx-service-group: ${spring.application.name}
    

    至此,Seata 的环境就搭建完成了!

    • 在 OrderController 的 save() 方法上添加 @GlobalTransactional 注解
    • 然后重新启动,访问:http://localhost:8090/save 报错
    • 检查数据库,结果发现 orders 和 pay 表都没有添加数据
    展开全文
  • seata0.9.0

    2020-05-21 14:04:18
    seata0.9.0
  • seata0.8.1

    2020-05-21 14:02:10
    seata0.8.1
  • Spring Cloud 使用 Seata 实现分布式事务,Nacos 作为 Seata 配置中心 使用 Seata 作为分布式事务组件,配置中心和注册中心使用 Nacos,使用 MySQL 数据库和 MyBatis,同时使用 Nacos 作为 Seata 的配置中心 ...

    Spring Cloud 使用 Seata 实现分布式事务,Nacos 作为 Seata 配置中心

    使用 Seata 作为分布式事务组件,配置中心和注册中心使用 Nacos,使用 MySQL 数据库和 MyBatis,同时使用 Nacos 作为 Seata 的配置中心

    至于对Nacos和Seata的介绍,请移步GitHub官网:


    
Nacos:https://nacos.io/zh-cn/docs/quick-start.html


    
Seata:https://github.com/seata/seata/wiki/Home_Chinese



    Nacos Server下载地址:https://github.com/alibaba/nacos/releases

    Seata Server下载地址:https://github.com/seata/seata/releases

    文章中的源码在GitHub

    环境准备

    创建数据库及表(在官方demo中的all_in_one.sql)

    • 要求:带有InnoDB引擎的MySQL。

    注意:实际上,示例用例中的3个服务应该有3个数据库。但是,我们只需创建一个数据库并配置3个数据源即可。

    • 业务表
    create schema db_account;
    use db_account;
    
    CREATE TABLE `account_tbl` (
           `id` int(11) NOT NULL AUTO_INCREMENT,
           `user_id` varchar(255) DEFAULT NULL,
           `money` int(11) DEFAULT 0,
           PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    INSERT INTO account_tbl (id, user_id, money) VALUES (1, '1001', 10000);
    INSERT INTO account_tbl (id, user_id, money) VALUES (2, '1002', 10000);
    
    CREATE TABLE `undo_log` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `branch_id` bigint(20) NOT NULL,
      `xid` varchar(100) NOT NULL,
      `rollback_info` longblob NOT NULL,
      `log_status` int(11) NOT NULL,
      `log_created` datetime NOT NULL,
      `log_modified` datetime NOT NULL,
      `ext` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    
    create schema db_order;
    
    use db_order;
    
    CREATE TABLE `order_tbl` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `user_id` varchar(255) DEFAULT NULL,
      `commodity_code` varchar(255) DEFAULT NULL,
      `count` int(11) DEFAULT '0',
      `money` int(11) DEFAULT '0',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    
    CREATE TABLE `undo_log` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `branch_id` bigint(20) NOT NULL,
      `xid` varchar(100) NOT NULL,
      `rollback_info` longblob NOT NULL,
      `log_status` int(11) NOT NULL,
      `log_created` datetime NOT NULL,
      `log_modified` datetime NOT NULL,
      `ext` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    
    create schema db_storage;
    
    use db_storage;
    
    CREATE TABLE `storage_tbl` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `commodity_code` varchar(255) DEFAULT NULL,
      `count` int(11) DEFAULT '0',
      PRIMARY KEY (`id`),
      UNIQUE KEY `commodity_code` (`commodity_code`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
    
    INSERT INTO storage_tbl (id, commodity_code, count) VALUES (1, '2001', 1000);
    
    CREATE TABLE `undo_log` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `branch_id` bigint(20) NOT NULL,
      `xid` varchar(100) NOT NULL,
      `rollback_info` longblob NOT NULL,
      `log_status` int(11) NOT NULL,
      `log_created` datetime NOT NULL,
      `log_modified` datetime NOT NULL,
      `ext` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

     

    启动 Nacos

    这里的 Nacos 配置使用的是本地启动的,nacos.config 的 Group, namespace 都是默认的,如果需要可以修改成自己对应的,可以参考官方文档:https://nacos.io/zh-cn/docs/quick-start.html

    sh startup.sh -m standalone

    启动 Seata Server

    1. 在 Seata Release 下载最新版的 Seata Server 并解压得到如下目录:
      .
      ├──bin
      ├──conf
      ├──file_store
      └──lib
    2. 修改 conf/registry.conf 配置,将 type 改为 nacos
    registry {
      type = "nacos"
    
      nacos {
        serverAddr = "localhost"
        namespace = "public"
        cluster = "default"
      }
    }
    
    config {
      type = "nacos"
    
      nacos {
        serverAddr = "localhost"
        namespace = "public"
        cluster = "default"
      }
    }
    
    1. 修改 conf/nacos-config.txt配置

    修改 service.vgroup_mapping为自己应用对应的名称;如果有多个服务,添加相应的配置(service.vgroup_mapping后面的值要与spring.cloud.alibaba.seata.tx-service-group对应的值匹配)如:

    service.vgroup_mapping.my_test_tx_group=default
    
    //改为 
    
    service.vgroup_mapping.storage-service-fescar-service-group=default
    service.vgroup_mapping.order-service-fescar-service-group=default
    service.vgroup_mapping.business-service-fescar-service-group=default
    service.vgroup_mapping.account-service-fescar-service-group=default
    

    也可以在 Nacos 配置页面添加,data-id 为 service.vgroup_mapping.${YOUR_SERVICE_NAME}-fescar-service-group, group 为 SEATA_GROUP, 如果不添加该配置,启动后会提示no available server to connect

    注意配置文件末尾有空行,需要删除,否则会提示失败,尽管实际上是成功的

    1. 将 Seata 配置添加到 Nacos 中
    cd conf
    sh nacos-config.sh localhost

    成功后会提示

    init nacos config finished, please start seata-server

    在 Nacos 管理页面应该可以看到有 47 个 Group 为SEATA_GROUP的配置

    1. 启动 Seata Server
    cd ..
    sh ./bin/seata-server.sh 8091 file

    启动后在 Nacos 的服务列表下面可以看到一个名为serverAddr的服务

    用例

    参考官网中用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

    • 存储服务:扣除给定商品的存储数量。
    • 订单服务:根据购买请求创建订单。
    • 帐户服务:借记用户帐户的余额。

    请求逻辑

    分别启动:

    ExampleBusinessApplication
    ExampleAccountApplication
    ExampleOrderApplication
    ExampleStorageApplication
    

    其中ExampleBusinessApplication服务中暴露了两个主要测试接口

    http://127.0.0.1:8084/purchase/commit接口是可以正常执行完成的方法

    http://127.0.0.1:8084/purchase/rollback接口是会发生异常并正常回滚的方法

     

    需要修改的文件:

    1、各个服务中的application.properties的spring.cloud.alibaba.seata.tx-service-group

    2、各个服务中的registry.conf的type

     

    3、seata-server中的conf/nacos-config.txt的service.vgroup_mapping

    文章参考:

    https://github.com/helloworlde/spring-cloud-alibaba-component/tree/master/cloud-seata-nacos

    展开全文
  • seata 阿里开源1.3.0版本源码及seata-server,seata 阿里开源1.3.0版本源码及seata-serverseata 阿里开源1.3.0版本源码及seata-serverseata 阿里开源1.3.0版本源码及seata-server
  • no available service ‘default’ found ...NotSupportYetException: not support register type: null can not register RM,err:can not connect to ...Could not initialize class io.seata.rm.datasource.undo.
  • alibaba seata

    2020-05-07 23:49:29
    implementation 'org.springframework.cloud:spring-cloud-starter-alibaba-nacos-discovery:0.2.1.RELEASE' ...implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-seata:2.1.1.RELEASE'
  • Seata: Simple Extensible Autonomous Transaction Architecture What is Seata? A distributed transaction solution with high performance and ease of use for microservices architecture. Distributed ...
  • seata.zip

    2020-04-15 13:36:25
    seata-server-0.7.1.zip seata-server-0.9.0.zip seata-server-1.0.0.zip
  • seata教程-Seata 1.0版本整合教程

    千次阅读 2019-12-27 09:10:45
    seata 1.0教程 技术选型及版本 spring-cloud-starter-alibaba-seata spring-cloud-alibaba:1.5.1.RELEASE springcloud:Edgware.SR4 seata-all:1.0.0 springboot:1.5.4 mybatis-spring-boot-starter.version:...
  • seata 使用oracle_Seata

    2021-01-30 08:11:18
    seata简介Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。微服务的分布式事务问题传统的...
  • Seata实战-分布式事务简介及demo上手

    万次阅读 多人点赞 2019-04-09 16:43:11
    文章目录Seata简介分布式事务产生背景数据库的水平分割微服务化分布式事务理论基础两阶段提交(2pc)TCCSeata解决方案第一阶段第二阶段Demo上手-Dubbo集成Seata配置修改启动测试 Seata简介 Seata(Simple Extensible ...
  • seata 官网

    千次阅读 2020-06-07 09:43:33
    http://seata.io/ http://seata.io/zh-cn/
  • seata1.4.0.txt

    2021-03-23 16:23:25
    seata1.4.0
  • seata1.3.0.txt

    2021-03-23 16:22:08
    seata1.3.0
  • seata学习.rar

    2021-02-25 13:37:12
    seata+nacos初体验seata+nacos初体验seata+nacos初体验seata+nacos初体验seata+nacos初体验
  • seata-server

    2020-12-31 10:45:03
    seata-server 1.4.0 与启动教程 gitHub卡
  • Seata简介

    2021-01-02 17:20:50
    文章目录一、seata是什么二、seata模块三、seata四种模式1、AT 模式2、TCC 模式3、Saga 模式4、Seata XA 模式 一、seata是什么 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务...
  • 安装seata与搭建seata服务

    千次阅读 2020-05-24 18:55:32
    第二步:解压 seata-server-0.9.0 第三步:运行bin下的seata-server.bat 2)涉及到分布式事务的数据库添加表 seata需要用到额外的一张数据库表,在需要分布式事务的数据库中执行如下sql创建表即可: -- 注意此处...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,562
精华内容 3,424
热门标签
关键字:

seata