美文网首页
微服务 15: Seata AT模式 核心代码(文末有项目连接)

微服务 15: Seata AT模式 核心代码(文末有项目连接)

作者: _River_ | 来源:发表于2021-04-17 22:55 被阅读0次
    文章知识来源主要来源于:赵俊夫先生的博客  以下为原文链接
    https://blog.csdn.net/u011177064/category_9572944.html
    
    seata官方文档:
    http://seata.io/zh-cn/docs/overview/what-is-seata.html
    
    1:为什么选择AT模式
    经过介绍对AT模式的优缺点都很明显。
    优点:
        1:完全没有代码嵌入 使用简单粗暴
        2:通过加锁的方式,可以保证分布式事务的强一致性。
    
    缺点:由于数据库加锁太多 导致高并发场景下效率偏低
    
    对于:普通公司的业务区场景已经是完全够用了
    
    2:Seata 的AT模式 下载资源
    使用官方Demo
    https://github.com/seata/seata-samples/tree/master/springboot-mybatis
    
    如果下载单个Demo模块的文件:
     
     1:进入网站 http://kinolien.github.io/gitzip/?
    
    2:第一次使用需要Github秘钥
        如何获取Github API Accsess token
        Github->Settings->Developer settings -> Personal accss tokens 
        ghp_pEumyQ5cGgpWkfHXHuDVmUR6JoWQGt0qiXXX
    
    3:输入  https://github.com/seata/seata-samples 
        点击 Search 下载 
    

    下载seata服务端
    从 https://github.com/seata/seata/releases,下载服务器软件包,将其解压缩。
    
    点击 seata/bin/ 下的 seata-server.bat 进行启动
    启动端口 8091  (window上找不到修改的方法)
    
    Usage: sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows) [options]
     Options:
       --host, -h
         The host to bind.
         Default: 0.0.0.0
       --port, -p
         The port to listen.
         Default: 8091
       --storeMode, -m
         log store mode : file、db
         Default: file
       --help 
    e.g.
    
    3:初始化分布式数据库SQL
    
    # Storage
    DROP SCHEMA IF EXISTS db_storage;
    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
      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,
      `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,
      PRIMARY KEY (`id`),
      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    # Order
    DROP SCHEMA IF EXISTS db_order;
    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
      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,
      `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;
    
    
    4:代码编写 Pom新增Maven包
    除 Feign服务 其他所有服务新增以下包
    1:需要使用mysql作为持久化数据库 mybatis作为持久化ORM框架
    2:引入seata 相关包
    
            <!-- Mysql 驱动包 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.44</version>
            </dependency>
     <!--        连接Mybatis持久层框架-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.4</version>
            </dependency>
            <!-- Mybatis 驱动包 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.6</version>
            </dependency>
    <!--        seata分布式事务-->
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>1.4.0</version>
            </dependency>
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-all</artifactId>
                <version>1.4.0</version>
            </dependency>
    
    5:代码编写 yml新增配置
    Feign服务 其他所有服务 yml新增以下配置
    
    实际上:config 与 registry   也能在\seata\conf 下的 registry.conf进行配置
    registry.conf 实际上可以和nacos 等服务注册发现中心配合使用
    这里为了方便 把config 与 registry 设置为 file类型
    
    相应的存储文件存放在 \seata\bin\sessionStore 的下  root.data
    
    seata:
      enabled: true
      application-id: nacos-consumer
      tx-service-group: my_test_tx_group
      service:
        vgroup-mapping:
          my_test_tx_group: default
        grouplist:
          default: 127.0.0.1:8091
    
      config:
        type: file
    
      registry:
        type: file
    
    
    6:修改service-consumer-demo 服务
    Seata 的事务上下文由 RootContext 来管理。
    应用开启一个全局事务后,RootContext 会自动绑定该事务的 XID,
    事务结束(提交或回滚完成),RootContext 会自动解绑 XID。
    该XID为1个分布式事务标识。
    
    seata文档
    http://seata.io/zh-cn/docs/user/microservice.html    
    
    XID 192.168.137.1:8091:126826542571458560 
    
    1:外部请求进来   由于使用了@GlobalTransactional注解  获取XID
    2:拦截器 在进行Feign调用的时候  如果XID存在 则打印一下  
    3:拦截器 在进行Feign调用的时候  把XID(不管是否存在)存放到到请求头
    
    注意:
    RootContext.getXID() 只是为了打印一下  而不是生成
    
    @Service
    @Slf4j
    public class BusinessServiceImpl implements BusinessService {
    
        @Autowired
        StorageFeignClientService storageFeignClientService;
    
        @Autowired
        ProviderFeignClientService providerFeignClientService;
    
        /**
         * 通过 storageFeignClientService 调用 storage服务 进行库存的减少
         * 通过 providerFeignClientService 调用 provider服务 进行订单的新增
         *
         * @param userId
         * @param commodityCode
         * @param orderCount
         */
        @Override
        @GlobalTransactional(rollbackFor = RuntimeException.class)
        public void purchase(String userId, String commodityCode, Integer orderCount) {
            //为了与日志区分 使用 System打印日志
    
           System.out.println("进入purchase 购买下单,模拟全局事务提交 请求");
            System.out.println("order XID " + RootContext.getXID());
    
            //storageService服务  db_storage库  storage_tbl表
            storageFeignClientService.seatadeductStorage(commodityCode, orderCount);
            //providerService服务   db_order库  order_tbl表
            providerFeignClientService.seataCreateOrder(userId, commodityCode, orderCount);
        }
    }
    
    /**
     * seata 的 AT 模式的 标志 存放到请求头里面
     * HeSuiJin
     */
    @Configuration
    public class FeignInterceptor implements RequestInterceptor {
        @Override
        public void apply(RequestTemplate requestTemplate) {
            String xid = RootContext.getXID();
            if (StringUtils.isNotBlank(xid)) {
                System.out.println("feign xid:"+xid);
            }
            requestTemplate.header(RootContext.KEY_XID, xid);
        }
    }
    
    7:新增服务 service-storage-demo
    该服务和 service-provider-demo 的结构一致 
    
    新增接口 /seataTest/deductStorage
    
    被storage-feign调用 进行库存的减少逻辑
    storageService服务  db_storage库  storage_tbl表
    
    8:修改service-provider-demo 服务
    新增接口 /seataTest/createOrder
    
    被provider-feign调用 进行订单的新增逻辑
    providerService服务   db_order库  order_tbl表
    
    @Slf4j
    @Service
    public class OrderServiceImpl implements OrderService {
    
        @Autowired
        private OrderMapper orderMapper;
    
        /**
         * 创建订单
         * @param userId
         * @param commodityCode
         * @param orderCount
         */
        @Override
        public void createOrder(String userId, String commodityCode, Integer orderCount) {
            BigDecimal orderMoney = new BigDecimal(orderCount).multiply(new BigDecimal(5));
            Order order = new Order();
            order.setUserId(userId);
            order.setCommodityCode(commodityCode);
            order.setCount(orderCount);
            order.setMoney(orderMoney);
            orderMapper.insert(order);
    
            //先进 storage服务 执行完逻辑
            //后进 provider服务 使得产生RuntimeException异常
            //storage服务 和 provider服务 都回滚
            if (true) {
                throw new RuntimeException("provider service  createOrder exception");
            }
        }
    }
    

    项目连接

    请配合项目代码食用效果更佳:
    项目地址:
    https://github.com/hesuijin/spring-cloud-alibaba-project
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/spring-cloud-alibaba-project.git
    
    https://github.com/hesuijin/spring-cloud-alibaba-project
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/spring-cloud-alibaba-project.git
    
    
    在service-storage-demo 模块下   
    在service-consumer-demo 模块下
    在service-provider-demo 模块下   
    在service-provider-demo-other 模块下

    相关文章

      网友评论

          本文标题:微服务 15: Seata AT模式 核心代码(文末有项目连接)

          本文链接:https://www.haomeiwen.com/subject/kxtslltx.html