• 使用Seata实现分布式事务


    一、安装与配置

      1、下载Seata

      点击进入下载页面(搜索Assets关键字,找到下载位置)

      2、修改配置文件

      D:\workset\javaset\seata-server-1.3.0\seata\conf\file.conf

    1、将mode = "file" 改为 mode = "db"
    2、修改db的配置信息 driverClassName = "com.mysql.cj.jdbc.Driver" url = "jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai" user = "sl" password = "123456"

      D:\workset\javaset\seata-server-1.3.0\seata\conf\registry.conf

    1、将registry的type = "file" 改为 type = "nacos"
    
    2、修改registry的nacos的配置信息
       application = "seata-server"
       serverAddr = "127.0.0.1:8848"
       group = "SEATA_GROUP"
       namespace = ""
       cluster = "default"
       username = "nacos"
       password = "nacos"
    
    3、将config的type = "file" 改为 type = "nacos"
    
    4、修改config的nacos的配置信息
       serverAddr = "127.0.0.1:8848"
       namespace = ""
       group = "SEATA_GROUP"
       username = "nacos"
       password = "nacos"

      备注:Nacos的下载与安装点击这里

      3、创建数据库表

      点击这里可以下载,脚本如下

    -- -------------------------------- 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(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;
    View Code

      双击启动 D:\workset\javaset\seata-server-1.3.0\seata\bin\seata-server.bat,Seata已经注册到Nacos里面了

      

       4、将配置注册到Nacos注册中心

       点击这里下载seata-1.3.0项目 下载后将整个script文件夹copy到Seata的根目录下

       源路径和copy后的路径分别为:

       原路径(seata-1.3.0源码中):C:\Users\shenl\Downloads\seata-1.3.0\script

       copy到的路径(seata-1.3.0的window安装路径):D:\workset\javaset\seata-server-1.3.0\seata\script

       修改文件 D:\workset\javaset\seata-server-1.3.0\seata\script\config-center\config.txt

    1、将store.mode=file 改为 store.mode=db
    
    2、修改db的配置信息
       store.db.datasource=druid
       store.db.dbType=mysql
       store.db.driverClassName=com.mysql.cj.jdbc.Driver
       store.db.url=jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
       store.db.user=sl
       store.db.password=123456
    

      备注:文件中的配置项service.vgroupMapping.my_test_tx_group=default需要与项目application.yaml中的配置一致

      在git客户端执行脚本:sh nacos-config.sh,config.txt中的所有参数就配置到了Nacos中了

      nacos-config.sh在此目录中找(D:\workset\javaset\seata-server-1.3.0\seata\script\config-center\nacos)

      5、在所有用到分布式事务的数据库中添加表

    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
    

    二、构建项目

      1、构建消费者项目

    application.properties
    # 应用名称
    spring.application.name=service-consumer
    # 应用服务 WEB 访问端口
    server.port=7010
    # Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
    # Nacos认证信息
    spring.cloud.nacos.discovery.username=nacos
    spring.cloud.nacos.discovery.password=nacos
    # Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    # 注册到 nacos 的指定 namespace,默认为 public
    spring.cloud.nacos.discovery.namespace=public
    
    
    #配置seata的注册中心
    seata.enabled=true
    seata.application-id=${spring.application.name}
    #配置事务分组
    seata.tx-service-group=my_test_tx_group #与script\config-center\config.txt中配置一致
    seata.registry.type=nacos
    seata.registry.nacos.server-addr=127.0.0.1:8848
    seata.registry.nacos.application=seata-server
    seata.registry.nacos.namespace=
    seata.registry.nacos.username=nacos
    seata.registry.nacos.password=nacos
    seata.registry.nacos.group=SEATA_GROUP
    #配置seata的配置中心
    seata.config.type=nacos
    seata.config.nacos.server-addr=127.0.0.1:8848
    seata.config.nacos.namespace=
    seata.config.nacos.username=nacos
    seata.config.nacos.password=nacos
    seata.config.nacos.group=SEATA_GROUP
    
    spring.datasource.url=jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    spring.datasource.username=sl
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    
    mybatis.type-aliases-package=com.leiyuke.cloud.consumer.pojo
    
    logging.level.root=info
    pom.xml
    <!-- nacos 服务注册发现(客户端)依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
    <!-- nacos-config 配置中心依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    
    <!-- seata 分布式事务依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    
    <!-- openfeign 远程调用依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    Application.java
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    IProductService.java
    @FeignClient(name = "service-product",path = "stock")
    public interface IProductService {
    
        @RequestMapping("/subStock")
        String subProduct();
    }
    ConsumerServiceImpl.java
    ... ...
    @GlobalTransactional //分布式事务注解
    public String addOrder() {
        Order order = orderMapper.getOrder();
        String value = order.getValue();
        Long num = Long.valueOf(value) + 1;
        String nextValue = String.format("%0"+6+"d",num);
        orderMapper.update(nextValue);//生成订单号,在数据库A
    
        //int a = 1/0;
    
        productService.subProduct();//库存减1,远程调用service-product微服务,在数据库B
        return "订单号为:"+nextValue;
    }
    ... ...

      2、构建生产者项目

    application.properties 只修改微服务名,端口,数据库配置,其余与消费者项目保持一致
    pom.xml 与消费者项目保持一致
    Application.java 启动类与消费者项目保持一致
    GoodsServiceImpl.java
    ... ...
    public void update() {
        //int a = 1/0;
        goodsMapper.update();
    }
    ... ...

    三、测试

      启动Nacos与Seata服务

      启动消费者与服务者两个项目

      1、给addOrder方法添加注解@GlobalTransactional

      分别去掉两处的注释(int a = 1/0)语句,使程序运行出错,两个数据中的值都没有改变,事务生效

      2、去掉addOrder方法的注解@GlobalTransactional

      分别去掉两处的注释(int a = 1/0)语句,使程序运行出错,发现订单库数据已修改,库存数据没有改变,事务不生效

     

      

  • 相关阅读:
    前端项目升级和降级依赖的最佳姿势
    如果你github提交代码,报错remote: Support for password authentication was removed on August 13, 2021.
    js将连接符命名和驼峰命名互转
    滚动条常用样式
    计算该浏览器中滚动条的默认宽度
    解决webpack-dev-server启动后localhost:port可以访问,IP:port不能访问的问题
    获取滚动条宽度的方法
    mysql中的数据类型
    数据库和表的操作
    mysql插入,删除,修改记录
  • 原文地址:https://www.cnblogs.com/javasl/p/13585712.html
Copyright © 2020-2023  润新知