一、Seata概念介绍

1、Seata 是什么?

Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。

seata下载http://seata.io/en-us/blog/download.html

选择自己的版本下载

seat中文文档:http://seata.io/zh-cn/docs/overview/what-is-seata.html

这里我们只介绍默认的AT模式与TCC模式。

2、Seata默认的AT模式介绍

前提

基于支持本地ACID事务的关系型数据库。

Java应用,通过JDBC访问数据库。

整体机制

两阶段提交协议的演变:

一阶段:

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

二阶段:

提交异步化,非常快速地完成。

回滚通过一阶段的回滚日志进行反向补偿。

写隔离

一阶段本地事务提交前,需要确保先拿到全局锁。拿不到全局锁,不能提交本地事务。拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。以一个示例来说明:

两个全局事务tx1和tx2,分别对a表的m字段进行更新操作,m的初始值1000。

tx1先开始,开启本地事务,拿到本地锁,更新操作m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁,本地提交释放本地锁。tx2后开始,开启本地事务,拿到本地锁,更新操作m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁,tx1全局提交前,该记录的全局锁被tx1持有,tx2需要重试等待全局锁。

tx1二阶段全局提交,释放全局锁 。tx2拿到全局锁 提交本地事务。

如果tx1的二阶段全局回滚,则tx1需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。

此时,如果tx2仍在等待该数据的全局锁,同时持有本地锁,则tx1的分支回滚会失败。分支的回滚会一直重试,直到tx2的全局锁等锁超时,放弃全局锁并回滚本地事务释放本地锁,tx1的分支回滚最终成功。

因为整个过程全局锁在tx1结束前一直是被tx1持有的,所以不会发生脏写的问题。

读隔离

在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata(AT 模式)的默认全局隔离级别是读未提交(Read Uncommitted)。

如果应用在特定场景下,必需要求全局的读已提交 ,目前Seata的方式是通过SELECT FOR UPDATE语句的代理。

SELECT FOR UPDATE语句的执行会申请全局锁 ,如果 全局锁被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE语句的本地执行)并重试。这个过程中,查询是被block住的,直到 全局锁拿到,即读取的相关数据是 已提交 的,才返回。

出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对FOR UPDATE 的SELECT语句。

3、TCC模式

回顾总览中的描述:一个分布式的全局事务,整体是两阶段提交的模型。全局事务是由若干分支事务组成的,分支事务要满足两阶段提交的模型要求,即需要每个分支事务都具备自己的:

  • 一阶段prepare行为
  • 二阶段commit或rollback行为

根据两阶段行为模式的不同,我们将分支事务划分为Automatic (Branch) Transaction Mode和Manual (Branch) Transaction Mode。

AT模式基于支持本地ACID事务的关系型数据库:

  • 一阶段prepare行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段commit行为:马上成功结束,自动异步批量清理回滚日志。
  • 二阶段rollback行为:通过回滚日志,自动生成补偿操作,完成数据回滚。

相应的TCC模式,不依赖于底层数据资源的事务支持:

  • 一阶段prepare行为:调用自定义的prepare逻辑。
  • 二阶段commit行为:调用自定义的commit逻辑。
  • 二阶段rollback行为:调用自定义的rollback逻辑。

所谓TCC模式,是指支持把自定义的分支事务纳入到全局事务的管理中。

二、seata服务端与nacos整合开发

1、配置seata的服务器端的数据库

https://github.com/seata/seata/tree/1.2.0/script

server端脚本如下,同时也支持docker、kubernatesa安装

 

第五章 Seata事务框架笔记

1)全局事务会话信息由3块内容构成,全局事务-->分支事务-->全局锁,对应表global_table、branch_table、lock_table 1;建立一个数据库名字随意用来做seata服务端的库,存储全局事务的会话信息

2)拿到服务端数据库的脚本文件执行并且建立表第1步三张表

https://github.com/seata/seata/tree/1.2.0/script/server/db 上面地址可以拿到数据库脚本,选择mysql然后自己执行。

2、修改服务端启动包的文件

1)启动包: seata-->conf-->file.conf,修改store.mode="db"。

2)修改file.conf自己的db连接信息

## transaction log store, only used in seata-server
store {
  ## store mode: file、db
  mode = "db"
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 1000
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}

这些信息是存在file.conf当中的也就是seata启动的时候会去读取这个配置文件;如果我们使用nacos可以把这些信息放到nacos的注册中心,从而实现动态更新。

如果你需要把这些信息放到nacos配置中心就需要修改seata-->conf-->registy.conf文件当中的注册中心和配置中心的信息,修改成为nacos;(为什么需要修改config和register呢?因为如果你的seata服务器想要去配置中心读取配置,那么一定到得把自己注册到nacos;所以registy.conf当中需要配置注册中心的地址也需要配置配置中心的地址) 这样我们如果后面启动seata就可以看到他是作为了一个nacos的客户端注册到了nacos的注册中心的;记住这点seata已经可以作为客户端注册到nacos了。

3、Seata部署指南

Seata分TC、TM和RM三个角色,TC(Server端)为单独服务端部署,TM和RM(Client端)由业务系统集成。

资源目录介绍:https://github.com/seata/seata/tree/1.2.0/script

Client:存放client端sql脚本,参数配置。

config-center:各个配置中心参数导入脚本,config.txt(包含server和client,原名nacos-config.txt)为通用参数文件。

Server:server端数据库脚本及各个容器配置。

注意事项

seata-spring-boot-starter

内置GlobalTransactionScanner自动初始化功能,若外部实现初始化,请参考SeataAutoConfiguration保证依赖加载顺序

默认开启数据源自动代理,可配置seata.enable-auto-data-source-proxy: false关闭

spring-cloud-alibaba-seata

查看版本说明 2.1.0内嵌seata-all 0.7.1,2.1.1内嵌seata-all 0.9.0,2.2.0内嵌seata-spring-boot-starter 1.0.0

2.1.0和2.1.1兼容starter解决方案:

2.1.0和2.1.1兼容starter解决方案: @SpringBootApplication注解内exclude掉spring-cloud-alibaba-seata内的com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration

3、把配置信息上传到nacos配置中心

第一步:启动nacos

第二步:访问https://github.com/seata/seata/tree/1.2.0/script/config-center到这个地址上面获取config.txt,然后把config.txt复制到idea当中去编辑,推荐使用idea编辑,因为可能有编码原因,保留自己想要的信息。

这里给出的是精简后的,你们需要自己对应修改自己的信息;主要是数据库配置信息。注意这些信息是服务器端和客户端都要使用的;由于上面我们已经把seata注册到了nacos;所以他的file.conf当中的信息可以直接从nacos读取;也就是下面我们配置的信息;换句话说如果你配置了seata作为nacos的一个客户端去读取配置那么file.conf可以不用配置了;这两步是重复的;这也是网上很多资料没有说明的;换成大白话的意思就是你如果配置了registy.conf那么file.conf当中的信息基本无效——都是从配置中心读取;甚至可以删了file.conf;你们可以自己测试;如果你不配置registy.conf,那么seata就会从file.conf当中读取配置;所以file.conf和registy.conf其实只需要配置一个。

3.1、精简后的配置如下

#事务分组——my_test_tx_group 这值会在我们客户端对应,需要注意
service.vgroupMapping.my_test_tx_group=default
service.default.grouplist=127.0.0.1:8091
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://ip::3306/seata?useUnicode=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

那么这些精简后的配置如何传到nacos呢?

https://github.com/seata/seata/tree/1.2.0/script/config-center 从上面这个地址下载nacos文件下面的nacos-config.sh文件然后执行sh 你下载后的路径/nacos-config.sh;当然如果你的nacos地址不是默认的,需要修改naocs-config.sh当中指定你的路径;也可以在sh命令后面指定;

 https://github.com/seata/seata/tree/1.2.0/script/config-center这个地址里面有个README.md文件有说明。

3.2、Nacos执行shell命令配置:

bash
sh ${SEATAPATH}/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t namespace

参数说明:

-h: host, the default value is localhost

-p: port, the default value is 8848

-g: Configure grouping, the default value is 'SEATA_GROUP'

-t: Tenant information, corresponding to the namespace ID field of Nacos, the default value is ''

注意:

这里我们在Nacos配置里面创建一个namespace叫seata,ID为seata_id

3.3、cd到Nacos文件夹所在目录执行命令:

sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t seata_id

 

第五章 Seata事务框架笔记

如果使用的都是默认配置则可以不指定参数,因为nacos-config.sh脚本里面已经包含了默认参数。

if [[ -z ${host} ]]; then

    host=localhost

fi

if [[ -z ${port} ]]; then

    port=8848

fi

if [[ -z ${group} ]]; then

    group="SEATA_GROUP"

fi

if [[ -z ${tenant} ]]; then

    tenant=""

Fi

执行完成之后,你可以看到nacos的配置中心上面多了很多配置;注意这个时候seata服务器用的就是这些配置了。你可以修改一个错误的试试是不能启动seata服务器的。

 

第五章 Seata事务框架笔记

4、启动seata服务器

讲道理可以启动成功——注意不要用jdk11(测试过有问题)

直接双击seata-server.bat就可以默认启动,默认端口为8091,如果想修改端口,也可以用配置项启动,参数如下:

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

例如需要修改默认端口:

seata-server.bat -p 18091

sh seata-server.sh -p 18091

不过linux中默认不会后台启动,需要启动命令支持一下:

nohup sh seata-server.sh -p 18091 > catalina.out 2>&1 &

当我们看到如下日志信息代表启动成功:

:RegisterTMRequest{applicationId='uni-meta-consumer', transactionServiceGroup='my_test_tx_group'},

channel:[id: 0x8aedbc89, L:/192.168.41.241:8091 - R:/192.168.41.241:61720]

 

三、新建seata客户端模块

1、建立微服务项目springcloudalibaba-seata

注意这里测试我们使用版本信息如下:

Springcloud版本:Hoxton.SR3

spring-cloud-alibaba:2.2.0.RELEASE

Springboot版本:2.2.6.RELEASE

io.seata版本:1.2.0

1.1、添加pom依赖

maven引入seata-spring-boot-starter、spring-cloud-alibaba-seata这两个jar其中seata-spring-boot-starter选择你对应的seata版本比如1.2。

但是spring-cloud-alibaba-seata这个jar当中自动依赖了seata-spring-boot-starter但是版本对应不上;比如spring-cloud-alibaba-seata当中依赖的seata-spring-boot-starter可能是0.9;所以需要剔除他

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.0.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter -->
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.2.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-seata -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
        <exclusions>
            <exclusion>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

1.2、配置客户端的seata

如果你的项目已经成为了nacos的客户端,那么直接可以从nacos读取第三步当中上传到nacos的各种配置;那么如何读取呢? 首先得在客户端进行配置告诉seata客户端需要去nacos注册中心去读取seata的配置。

配置客户端的yml读取nacos上的seata的配置

打开这个地址https://github.com/seata/seata/tree/1.2.0/script/client 找到spring文件夹,找到application.yml;这个yml是通用配置;你需要精简;我给出精简后的配置如下:

server:
  port: 7100
seata:
  enabled: true
  application-id: springcloudalibaba-seata
  tx-service-group: my_test_tx_group
  enable-auto-data-source-proxy: true
  use-jdk-proxy: true
  config:
    type: nacos
    nacos:
      namespace: 162ee4db-c19c-40df-8b90-3f6db9610bd1
      serverAddr: 192.168.41.241:8848
      group: SEATA_GROUP
      userName: ""
      password: ""
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.41.241:8848
      namespace: 162ee4db-c19c-40df-8b90-3f6db9610bd1
      userName: ""
      password: ""
# The bean 'springcloudalibaba-provider-user.FeignClientSpecification' could not be registered.
# A bean with that name has already been defined and overriding is disabled.
spring:
  main:
    allow-bean-definition-overriding: true

bootstrap.yml配置信息:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.41.241:8848
      config: #这里一定记得配置
        server-addr: 192.168.41.241:8848
  application:
    name: springcloudalibaba-seata

特别注意:

这个配置需要在你的每一个参与分布式事务的项目当中加上——直接写到项目的yml当中就可以了。这个配置的意思就是让我们的seata客户端直接从配置中心拉取配置;

2、分布式事务测试场景设计

当然分布式事务肯定需要是至少两个项目,所以你在两个项目当中都加上这些依赖,如果你是nacos的自然还需要加上其他jar;详见下文。

建好项目之后,我们模拟一个分布式事务的场景 比如A调用B项目,在B项目里面操作数据库加上A项目当中的AController当中的a();调用B项目当中的BController当中的b();b方法操作数据库;那么则在A项目当中的AController的a()上面加上@GlobalTransactional这个注解

@RestController
public class A{
    @GlobalTransactional
    @GetMapping("xxxxxx")
    public string a(){
        通过feign调用b
    }
}

注意:@GlobalTransactional注解就是分布式事务注解。

2.1、服务消费端调用提供端/update接口

springcloudalibaba-seata模块controller层入口代码如下:

@RestController
@RefreshScope
@RequestMapping("seata")
public class IndexController {
    @Autowired
    ProductService productService;
    @GetMapping("/update")
    public int update(@RequestParam String name){
        int result =  productService.update(name);
        return result;
    }
}

2.2、业务层代码实现

特别注意:seata事务注解@GlobalTransactional 标注的方法将会被seata事务框架管理,方法调用我们通过feign的方式实现远程调用(访问springcloudalibaba-seata-provider模块的updata接口)

@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    SeataFeignClient userCenterFeignClient;
    @GlobalTransactional // seata事务注解
    public int update(String name) {
        // 使用feign的方式实现调用
        int result = userCenterFeignClient.update(name);
        System.out.println("ProductServiceImpl----update name==>"+name+", result==="+result);
        if(name.equals("500"))
            System.out.println("xxxxxxxxxxxx"+1/0);
        return result;
    }
}

2.3、FeignClien接口层代码见:

com.chj.feign.SeataFeignClient

//@FeignClient(name = "springcloudalibaba-provider-user")
@FeignClient(name = "springcloudalibaba-seata-provider")
//@FeignClient(name = "springcloudalibaba-provider-user", configuration = UserFeignLogConfiguration.class) //细粒度代码方式配置
//@FeignClient(name = "springcloudalibaba-provider-user", configuration = GlobalFeignConfiguration.class) // 代码方式全局配置
//@FeignClient(name = "springcloudalibaba-provider-user",
////    fallback = UserCenterFeignClientFallback.class,
//        fallbackFactory = UserCenterFeignClientFallbackFactory.class
//)
public interface SeataFeignClient {
    /**
     * http://springcloudalibaba-seata-provider/update?name=xxx
     */
    @GetMapping("provider/update")
    int update(@RequestParam String name);
}

至此seata client端的第一个业务调用端的代码实现已经ok,下面我们接着编写服务提供端代码。

四、服务提供端代码实现

1、新建seata业务端模块

新建模块springcloudalibaba-seata-provider

1.1、添加pom依赖:

注意这里测试我们使用版本信息如下:

Springcloud版本:Hoxton.SR3

spring-cloud-alibaba:2.2.0.RELEASE

Springboot版本:2.2.6.RELEASE

Mybatis通用版本:tk.mybatis-2.1.5

mybatis.generator<version>1.3.6</version> --通过maven方式MGB

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
  </parent>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR3</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.2.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-context -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      <exclusions>
        <exclusion>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>com.alibaba.nacos</groupId>
      <artifactId>nacos-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- Feign默认支持了 httpclient与okhttp -->
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-httpclient</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter -->
    <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-spring-boot-starter</artifactId>
      <version>1.2.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-seata -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-seata</artifactId>
      <exclusions>
        <exclusion>
          <groupId>io.seata</groupId>
          <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- 方式二 : 集成通用mapper -->
    <dependency>
      <groupId>tk.mybatis</groupId>
      <artifactId>mapper-spring-boot-starter</artifactId>
      <version>2.1.5</version>
    </dependency>
    <!-- mysql驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.29</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>
  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <!-- 使用maven方式执行MBG,引入插件 -->
      <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.6</version>
        <configuration>
          <configurationFile>
            ${basedir}/src/main/resources/generator/generatorConfig.xml
          </configurationFile>
          <overwrite>true</overwrite>
          <verbose>true</verbose>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
          </dependency>
          <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.1.5</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>

1.2、添加配置信息

application.yml

server:
  port: 7200
spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_provider?characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: root
  main:
    allow-bean-definition-overriding: true
seata:
  enabled: true
  application-id: springcloudalibaba-seata-provider
  tx-service-group: my_test_tx_group
  enable-auto-data-source-proxy: true
  use-jdk-proxy: true
  config:
    type: nacos
    nacos:
      namespace: 162ee4db-c19c-40df-8b90-3f6db9610bd1
      serverAddr: 192.168.41.241:8848
      group: SEATA_GROUP
      userName: ""
      password: ""
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.41.241:8848
      namespace: 162ee4db-c19c-40df-8b90-3f6db9610bd1
      userName: ""
      password: ""
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
# mybatis配置mapper 可以通过注解代替 @MapperScan(basePackages = "扫描包")

bootstrap.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.41.241:8848
      config: #这里一定记得配置
        server-addr: 192.168.41.241:8848
  application:
    name: springcloudalibaba-seata-provider

1.3、启动来代码

注意添加Nacos服务发现注解@EnableDiscoveryClient与mybatis扫描注解

@MapperScan(basePackages = "com.chj.dao")
@SpringBootApplication
@EnableDiscoveryClient
public class SeataProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(SeataProviderApplication.class, args);
    }
}

1.4、业务端代码实现

控制访问代码如下:com.chj.controller.SeataController

@Slf4j
@RestController
@RefreshScope
@RequestMapping("provider")
public class SeataController {
    @Autowired
    ProductService productService;
    @GetMapping("/update")
    public int update(@RequestParam String name){
        int result =  productService.update(name);
        log.info("springcloudalibaba-provider-user--update--result===="+result);
        return result;
    }
}

注意:该接口是暴露给服务消费端(springcloudalibaba-seata)调用的

五、启动服务测试

需要在你的客户端操作的数据库当中建立undo_log表;这个表用来实现sql反向补偿也就是回滚的信息,这个表的见表语句——https://github.com/seata/seata/tree/1.2.0/script/client/at/db 注意是建立在你的微服务所对应的库中;比如你的B服务链接了X库;那么则在x库中建立这个表;如果你的A服务链接了Y库;则Y库也需要这个表

1、环境准备

首先启动Nacos服务端与seata服务端

然后分别启动seata的服务提供端与服务消费端

2、访问接口测试

2.1、正常参数结果测试

因为我们代码中设置了当参数值为500的时候,会抛出异常,然后数据库数据应该回滚。

访问:http://localhost:7100/seata/update?name=200

数据可以正常修改,查看数据库如下所示:

 

第五章 Seata事务框架笔记

2.2、异常情况测试

这次我们参数值设为500,再次访问结果如下:

 

第五章 Seata事务框架笔记

我们可以通过debug模式,在事务方式抛出异常前查看数据库信息:

 

第五章 Seata事务框架笔记

可以看到在代码执行结束前,数据库数据已经别修改了,接着我们让代码执行结束,再次查看数据库:

 

第五章 Seata事务框架笔记

发现数据有回到了200,说明我们的代码确实回滚了这个事务。

 

第五章 Seata事务框架笔记

查看undo_log表信息:

 

第五章 Seata事务框架笔记

其中字段rollback字段的内ring信息如下:

INSERT INTO seata_provider.undo_log ( id, branch_id, xid, context, rollback_info, log_status, log_created, log_modified ) VALUES ( 7, 2012067280, '192.168.41.241:8091:2012067277', 'serializer=jackson', 0 x7B7D, 1, '2020-05-19 16:34:21', '2020-05-19 16:34:21' );

六、Seata源码方式配置服务

1、下载源码构建环境

第一步:GitHub下载源码:这里我们选择develop版本:

下载地址:http://seata.io/en-us/blog/download.html

第二步:执行mvnw.cmd --下载构建maven环境,统一maven环境配置管理(自带)

备注:mvnw.cmd里面配置会下载maven3.6.0建立自己的maven仓库,这里我们可以自己配置指定maven地址:在seata-1.2.0\.mvn\wrapper\maven-wrapper.properties文件中可以指定maven安装包地址。

distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip

这里我们改为自己下载好的maven地址:

distributionUrl=file://D:/tools/maven/apache-maven-3.6.3-bin.zip

第三步:maven环境准备过程会很漫长,构建好之后我们可以找到server项目里面的启动来Server,执行里面的main方法即可启动代码。

第四步:在启动之前先要修改配置

file.conf与registry.conf

2、新建业务端模块测试

Seata之所以可以完成分布式事务是因为——它的数据源需要去代理

Seata客户端配置文件application.yml中的数据源代理配置如下所示:

seata:
  enabled: true
  application-id: springcloudalibaba-seata
  tx-service-group: my_test_tx_group
  enable-auto-data-source-proxy: true # 开启数据源代理(实现分布式事务的原因)
  use-jdk-proxy: true

在seata1.0之前需要代码里面配置数据源代理代码实现,但是在1.0之后,spring帮助seata代理了数据源,所以我们只需要配置enable-auto-data-source-proxy: true代码里面注入数据源的时候可以直接注入:

代码示例如下:

@Configuration
@MapperScan(basePackages = "com.chj.dao.user", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config {
    @Bean(name = "test1DataSource")
    @Primary
    public DataSource testDataSource(DBConfig1 dbConfig1) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(dbConfig1.getDriverClassName());
        druidDataSource.setUrl(dbConfig1.getJdbcUrl());
        druidDataSource.setPassword(dbConfig1.getPassword());
        druidDataSource.setUsername(dbConfig1.getUsername());
        return druidDataSource;
    }
    // 代理数据源
//    @Bean
//    public DataSourceProxy dataSourceProxy(DataSource dataSource){
//        return new DataSourceProxy(dataSource);
//    }
    @Bean(name = "test1SqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/user/*.xml"));
        return bean.getObject();
    }

备注:

File.conf 与registry.conf其实是重复的配置,如果是使用了配置中心则file的方式就不需要了。

相关文章:

  • 2021-07-17
  • 2021-09-22
  • 2021-11-27
  • 2021-06-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-09
猜你喜欢
  • 2021-04-16
  • 2022-03-09
  • 2021-09-15
  • 2022-12-23
  • 2022-12-23
  • 2021-11-16
  • 2021-07-08
相关资源
相似解决方案