zoukankan      html  css  js  c++  java
  • Spring Cloud Alibaba学习08Seata基本使用

    Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

    Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

    官网地址:Seata

    Seata术语:

    TC (Transaction Coordinator) - 事务协调者
    维护全局和分支事务的状态,驱动全局事务提交或回滚。

    TM (Transaction Manager) - 事务管理器
    定义全局事务的范围:开始全局事务、提交或回滚全局事务。

    RM (Resource Manager) - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

    AT 模式
    1.前提
    基于支持本地 ACID 事务的关系型数据库。
    Java 应用,通过 JDBC 访问数据库。


    2.整体机制
    两阶段提交协议的演变:

    一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

    二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。

    3.流程:

    3.1 全局事务开启:TM向TC发起全局事务,TC生成全局事务id(xid),全局事务id可以在调用链中进行传播。

    3.2 一阶段提交:

    过程:

    对于如下sql语句:

    update product set name = 'GTS' where name = 'TXC';

    (1)解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。

    (2)查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

    (3)执行业务 SQL:更新这条记录的 name 为 'GTS'。

    (4)查询后镜像:根据前镜像的结果,通过 主键 定位数据。

    (5)插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。

    (6)提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁

    (7)本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。

    (8)将本地事务提交的结果上报给 TC。

    3.3.1 二阶段提交:

    收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。

    异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

    3.3.2 二阶段回滚:

    收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。

    通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。

    数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。

    根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

    update product set name = 'TXC' where id = 1;

    提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

    搭建Seata Server

    下载地址:Tags · seata/seata (github.com)

    配置conf/file.conf

    创建seata数据库:

    脚本地址:seata/script/server/db at develop · seata/seata (github.com)

    创建seata数据库,并执行如下(MySQL)脚本:

    -- -------------------------------- The script used when storeMode is 'db' --------------------------------
    -- the table to store GlobalSession data
    CREATE TABLE IF NOT EXISTS `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;
    
    -- the table to store BranchSession data
    CREATE TABLE IF NOT EXISTS `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;
    
    -- the table to store lock data
    CREATE TABLE IF NOT EXISTS `lock_table`
    (
        `row_key`        VARCHAR(128) NOT NULL,
        `xid`            VARCHAR(128),
        `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;
    
    CREATE TABLE IF NOT EXISTS `distributed_lock`
    (
        `lock_key`       CHAR(20) NOT NULL,
        `lock_value`     VARCHAR(20) NOT NULL,
        `expire`         BIGINT,
        primary key (`lock_key`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8mb4;
    
    INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
    INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
    INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
    INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

    执行完毕后,见如下四个表:

    Seata使用Nacos注册中心,配置文件为conf/registry.conf:

    Seata的配置,参考:Seata 参数配置

    在Nacos上创建一个配置文件:

    启动Seata,在bin目录运行如下命令:

    cd bin
    seata-server.bat -h 127.0.0.1 -p 8098

    启动后,可以在Nacos控制台看到seata服务已经注册成功。

    在业务数据库创建UNDO_LOG表,脚本见github:seata/mysql.sql at develop · seata/seata (github.com)

    -- for AT mode you must to init this sql for you business database. the seata server not need it.
    CREATE TABLE IF NOT EXISTS `undo_log`
    (
        `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
        `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
        `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
        `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
        `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
        `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

    代码示例:

    1、在cloud-goods中添加pom依赖:

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
        <version>2.2.0.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.4.2</version>
    </dependency>    

    2、添加配置:

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
        url: jdbc:mysql://localhost:3306/goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: false
    seata:
      enabled: true
      tx-service-group: fengmi_tx_group
      enable-auto-data-source-proxy: true
      service:
        vgroupMapping:
          my_test_tx_group: default
      config:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
          group: DEFAULT_GROUP
          namespace: public
          username: nacos
          password: nacos
          data-id: seataServer.properties
      registry: #从Nacos中发现seata server服务
        type: nacos
        nacos:
          application: seata-server
          server-addr: 127.0.0.1:8848
          namespace: public
          group: DEFAULT_GROUP
          username: nacos
          password: nacos

    在代码中可以使用@GlobalTransitional注解来实现分布式事务。

    package com.yas.service;
    
    import com.yas.Goods;
    import com.yas.mapper.GoodsMapper;
    import io.seata.spring.annotation.GlobalTransactional;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class GoodsService implements IGoodsService {
        @Autowired
        GoodsMapper goodsMapper;
    
        @Override
        @GlobalTransactional
        public String buy(){
            Integer result = goodsMapper.insert(new Goods("苹果",3999));
            System.out.println(result);
    
            int x = 1 / 0;
            System.out.println(x);
            return result+"";
        }
    }
  • 相关阅读:
    Single Number II
    Pascal's Triangle
    Remove Duplicates from Sorted Array
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Unique Paths
    Sort Colors
    Swap Nodes in Pairs
    Merge Two Sorted Lists
    Climbing Stairs
  • 原文地址:https://www.cnblogs.com/asenyang/p/15547238.html
Copyright © 2011-2022 走看看