SpringCloudAlibaba:分布式事务之Seata学习

目录

一、分布式事务基础

(一)事务

(二)本地事务

(三)分布式事务

二、Seata概述

1.Seata 的架构包含:

2.其工作原理为:

3.如果需要在 Spring Boot 应用中使用 Seata 进行分布式事务管理,主要步骤为:

1、下载seata并解压

2、修改配置文件

3、创建数据库

4、初始化seata在nacos的配置

5、启动seata服务

6、Seata实现分布式事务控制案例:


官网:https://seata.io/

一、分布式事务基础

(一)事务

事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。简单地说,事务提供一种“要么什么都不做,要么做全套”机制。

(二)本地事务

本地事物其实可以认为是数据库提供的事务机制。说到数据库事务就不得不说,数据库事务中的四大特性: A:原子性(Atomicity),一个事务中的所有操作,要么全部完成,要么全部不完成 C:一致性(Consistency),在一个事务执行之前和执行之后数据库都必须处于一致性状态 I:隔离性(Isolation),在并发环境中,当不同的事务同时操作相同的数据时,事务之间互不影响 D:持久性(Durability),指的是只要事务成功结束,它对数据库所做的更新就必须永久的保存下来 数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中 的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚

(三)分布式事务

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。 本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

二、Seata概述

Seata官方文档

Seata 是一款开源的分布式事务解决方案,它支持 AT、TCC、SAGA 等事务模式。在微服务架构下提供了分布式事务服务,解决了强一致性的业务处理问题。

1.Seata 的架构包含:

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

  • TM(Transaction Manager):控制分支事务,与 TC 交互以控制分支事务的提交、回滚等。

  • RM(Resource Manager):控制分支事务涉及的资源,与 TM 交互以控制分支事务。

2.其工作原理为:

  1. 应用开始全局事务,通过调用 TC 的 begin() 方法开启一个全局事务。

  2. 应用调用多个 TM 的 begin() 方法开启分支事务。TC 统一控制这些分支事务。

  3. 分支事务可以操作多个 RM 的资源,RM 参与者需要实现回滚和提交方法。

  4. 当应用结束分支事务并决定提交或回滚全局事务时,它会通知 TC。TC 将指示所有的 TM 提交或回滚相应的分支事务。

  5. TM 将指示 RM 执行实际的提交或回滚操作。

  6. 操作完成后,TC、TM 和 RM 会相互确认这些操作,以确保全局事务的最终一致性。

3.如果需要在 Spring Boot 应用中使用 Seata 进行分布式事务管理,主要步骤为:

1、下载seata并解压

1)下载

下载地址:Release v1.5.2 · seata/seata · GitHub

下载的版本参考:springcloudalibaba-seata版本参考

2)解压

2、修改配置文件

seata/conf/application.yml 进行修改

参考官网配置:[seata/script at master · seata/seata · GitHub]

修改 seata\conf\application.yml 的内容主要有以下三部分:

 application.yml

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash
      
console:
  user:
    username: seata
    password: seata      

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
      data-id: seataServer.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    #preferred-networks: 30.240.*
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
      cluster: default
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""

  server:
    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    recovery:
      handle-all-session-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis
    mode: db
    #session:
     # mode: file
    #lock:
     # mode: file
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
      user: root
      password: 123456
      min-conn: 5
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 100
      max-wait: 5000
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
  transport:
    rpc-tc-request-timeout: 30000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login      

3、创建数据库

创建上面配置中的 seata 数据库并根据解压路径 seata\script\server\db\mysql.sql 文件创建表

4、初始化seata在nacos的配置

1)进入到上面下载好的seata的解压后的seata\script\config-center\nacos目录中,

执行nacos-config.sh文件,进入当前层级的cmd执行下面带参数的命令,

就会将seata\script\config-center\config.txt文件中的配置全部写入到127.0.0.1:8848的nacos配置中心中。

启动参数说明如下: 

执行成功后可以打开Nacos的控制台,在配置列表中,可以看到初始化了很多Group为SEATA_GROUP的配置。

2)适当将seata\script\config-center\config.txt文件的默认配置删掉再去执行nacos-config.sh文件,否则配置中心的配置太多了,我自己的最终config.txt文件如下:

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client

#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h

#Log rule configuration, for client and server
log.exceptionRate=100

5、启动seata服务

两种启动方式:

  1. windows中双击seata\bin\ seata-server.bat 启动

  2. 使用命令启动如下;

    cd bin
    seata-server.bat -p 9000 -m file

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

6、Seata实现分布式事务控制案例:

案例解析:一个user模块,一个product模块,测试user通过feign调用product中的方法

第一步:在各个微服务中添加seata依赖

<!--seata依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

第二步:在各个微服务连接的数据库中添加undo_log表

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;

第三步:配置配置事务分组,配置seata注册中心和配置中心

spring:
  application:
    name: product-server
  datasource:
    url: jdbc:mysql://localhost:3306/product?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    # 以下的是每个微服务都要添加的配置
    alibaba:
      seata:
        tx-service-group: default_tx_group
# 配置seata注册中心和配置中心
seata:
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP

第四步:在被调用微服务里,编写方法

@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private ProductMapper productMapper;
    @PostMapping("/get1")
    public Integer update(@RequestBody Product product){
        int i = productMapper.updateNumByPId(product);
        return i;
    }
}

第五步:在调用者微服务里写feign接口,调用其他微服务的方法

@FeignClient(value = "product-server", path = "/product")
public interface ProductFeign {
    @PostMapping("/get1")
    void updateNumByPId(@RequestBody Product product);
}

第六步:在调用者微服务的serviceImpl,编写方法,在方法上加 @GlobalTransactional 注解 开启事务

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{
​
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ProductFeign productFeign;
​
​
    @Override
    @GlobalTransactional
    public void updateNumByPId(@RequestBody User user) {
        Product product = new Product();
        product.setPid(1);
        product.setNum(2000);
        productFeign.updateNumByPId(product);
        int a = 1 / 0;
        userMapper.updateById(user);
    }
}

第七步:controller测试(service层的除零那行打断点Debug测试)

@RestController
@RefreshScope // 在需要动态读取配置的类上添加此注解就可以(动态配置刷新)
@RequestMapping("/user")
public class UserController {
​
    @Autowired
    private UserService userService;
    
    // Seata实现分布式事务控制
    @PostMapping("/testSeata")
    public void testSeata(@RequestBody User user){
        userService.updateNumByPId(user);
    }
} 

结果:在没有出现除0异常的时候数据被正常修改,出现异常后不会继续往下执行,而是将上面的操作进行回滚。

猜你喜欢

转载自blog.csdn.net/Microhoo_/article/details/131035791
今日推荐