栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Seata事务管理---入门以及测试

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Seata事务管理---入门以及测试

1. Seata安装与配置

1.下载相关文件与源码: https://github.com/seata/seata/releases(记得源码也要下载,后续需要)

2.创建数据库表: 去源码文件夹里找到mysql.sql,用数据库可视化工具创建好表


3.修改配置文件file.conf: 修改mysql连接的配置信息,这个文件在conf目录下(不是源码)

dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&useSSL=false"
    user = "root"
    password = "123456"

4.前往nacos添加命名空间: 添加Seata命名空间,用来保存Seata相关的配置信息

5.修改配置文件register.conf: 将配置信息保存在nacos中,修改配置文件

6.将配置文件同步到nacos中: 前往源码找到config.txt文件,修改数据库连接信息,该文件在seata-1.4.2scriptconfig-center

7.将配置文件同步到nacos: 前往seata-1.4.2scriptconfig-centernacos下,打开git控制台,使用命令将信息上传到nacos里

#这里的命名空间ID,分组等信息要改成自己的
sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 43fd994f-7ee4-47f4-bd16-28b39f603c6d -u nacos -w nacos

8.检查配置文件是否同步: 去nacos里看配置列表

9.启动Seata: 前往bin目录下,点击启动文件seata-server.bat

2 分布式事务测试

1.搭建相关测试项目: 本次测试主要搭建两个服务,一个模拟库存服务,一个模拟订单服务,库存服务主要是减少库存,而订单服务则是生成相对应的订单。主要模拟的是一个下单的操作,就是说:一个下单操作,应该是减少相对应的商品库存信息,另一个则是生成相对应的订单信息。

项目gitee地址: https://gitee.com/longjiamou/spring-cloud-alibaba.git

部分主要代码和配置:

pom依赖导入: 主要是导入seata,nacos,mybatis-plus等相关依赖



    com.alibaba.cloud
    spring-cloud-starter-alibaba-seata
    
        
            seata-all
            io.seata
        
        
            io.seata
            seata-spring-boot-starter
        
    

<!--要与服务端的版本一致 -->

    io.seata
    seata-all
    1.4.2

相关seata配置编写:

seata:
  # 是否开启seata,默认true
  enabled: true
  application-id: seata-server
  # seata事务组的名称,一定要和config.tx(nacos)中配置的相同
  tx-service-group: default
  # 配置中心的配置
  config:
    # 使用类型nacos
    type: nacos
    # nacos作为配置中心的相关配置,需要和server在同一个注册中心下
    nacos:
      # 命名空间,需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      namespace: 43fd994f-7ee4-47f4-bd16-28b39f603c6d
      # 地址
      server-addr: localhost:8848
      # 组, 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      group: SEATA_GROUP
      # 用户名和密码
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      # 这里的名字一定要和seata服务端中的名称相同,默认是seata-server
      application: seata-server
      # 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      group: SEATA_GROUP
      namespace: 43fd994f-7ee4-47f4-bd16-28b39f603c6d
      username: nacos
      password: nacos
      server-addr: localhost:8848

生成订单接口编写: server-order-8082下的orderServiceImpl

@Transactional
public String add(@RequestBody Orders book){

    int insert = mapper.insert(book);
    System.out.println(insert);
    if(insert>0){
        return "创建订单成功";
    }else {
        return "创建订单失败";
    }
}

减少库存接口编写: 在这里使用了该方法作为seata的TM

@Transactional
@GlobalTransactional
public String BuyBook(Orders book){
    //1.查询相关书籍库存信息
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.eq("book_id",book.getBookId());
    Book book1 = mapper.selectOne(wrapper);

    //2.减少相关库存
    int i = book1.getBookCount() - book.getOrderCounts();
    book1.setBookCount(i);
    int i1 = mapper.update(book1,wrapper);

    //3.生成订单
    String order = feigin.add(book);

    System.out.println(order);
    return order;
}

2.测试1: 测试没有加全局事务管理并且让程序能正常运行

    @Transactional
//    @GlobalTransactional
    public String BuyBook(Orders book){
        System.out.println("全局事务ID:"+RootContext.getXID());
        //1.查询相关书籍库存信息
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("book_id",book.getBookId());
        Book book1 = mapper.selectOne(wrapper);

        //2.减少相关库存
        int i = book1.getBookCount() - book.getOrderCounts();
        book1.setBookCount(i);
        int i1 = mapper.update(book1,wrapper);

        //3.生成订单
        String order = feigin.add(book);

        System.out.println(order);
        return order;
    }



3.测试2: 测试没有加全局事务管理,当时服务出现了异常,比如读取超时等情况,这里让创建订单的响应时间为2秒,这时候就会出现超时情况,因为feigin的调用时间为1秒。

@Transactional
public String add(@RequestBody Orders book) throws InterruptedException {
    Thread.sleep(2000);
    int insert = mapper.insert(book);
    System.out.println(insert);
    if(insert>0){
        return "创建订单成功";
    }else {
        return "创建订单失败";
    }
}



3.测试3: 加了全局事务,并且让服务出错,看看事务是否回滚

@Transactional
@GlobalTransactional
public String BuyBook(Orders book){
    System.out.println("全局事务ID:"+RootContext.getXID());
    //1.查询相关书籍库存信息
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.eq("book_id",book.getBookId());
    Book book1 = mapper.selectOne(wrapper);

    //2.减少相关库存
    int i = book1.getBookCount() - book.getOrderCounts();
    book1.setBookCount(i);
    int i1 = mapper.update(book1,wrapper);

    //3.生成订单
    String order = feigin.add(book);

    System.out.println(order);
    return order;
}

//server-order-8082里的接口方法,模拟创建订单功能失败
    @Transactional
    public String add(@RequestBody Orders book){
        int i = 1/0;
        int insert = mapper.insert(book);
        System.out.println(insert);
        if(insert>0){
            return "创建订单成功";
        }else {
            return "创建订单失败";
        }
    }


3 AT模式原理以及过程(以本次测试为例)

       AT模式主要分为两个阶段,三张数据库表以及Seata中的三个角色(TC,TM,RM),每一个阶段都有不同的功能,每一张表都记录事务执行过程中所涉及到的一些数据,如:全局事务ID,子事务ID等等。


所涉及到的三张表如下:

global_table: 全局事务表,每当有一个全局事务发起后,就会在该表中记录全局事务的ID

branch_table: 分支事务表,记录每一个分支事务的ID,分支事务操作的哪个数据库等信息

lock_table: 全局锁


一阶段步骤:

  1. TM --> service-provider-8081:BuyBook()方法执行时,由于该方法具有@GlobalTranscational标志,该TM会向TC发起全局事务,生成XID(全局锁)
  2. RM --> service-provider-8081:BuyBook():写表,UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果
  3. RM --> service-order-8082:add():写表,UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果

简单理解: RM,即我们的本地事务,在写表的过程,Seata 会拦截业务SQL ,首先解析 SQL 语义,在业务数据被更新前,将其保存成before image (前置镜像),然后执行业务SQL ,在业务数据更新之后,再将其保存成after image (后置镜像),最后生成行锁。以上操作全部在一个数据库事务内完成(在seata数据库),这样保证了一阶段操作的原子性。


二阶段步骤(提交):

       因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。TM执行成功之后,通知TC全局提交,TC此时通知所有的RM提交成功,删除UNDO_LOG回滚日志。

二阶段步骤(失败回滚):

        TM执行失败,通知TC全局回滚,TC此时通知所有的RM进行回滚,根据UNDO_LOG反向操作,使用before image 还原业务数据,删除UNDO_LOG,但在还原前要首先要校验脏写,**对比“数据库当前业务数据”和 “after image”**来校验数据是否出现脏读现象,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/871608.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号