SpringBoot+Seata在多数据源和feign中的简单使用


SpringBoot+Seata简单使用


目录

  • seata执行过程
  • 安装seata
    • 下载seata
    • 使用自定义配置文件,NACOS为注册中心
  • 结合springboot实现AT模式
    • 1.多数据源
      • 引入依赖
      • bootstrap.yml配置
      • 在使用的方法上用@GlobalTransactional注解
      • 调用接口正常时
      • 调用接口报错时回滚
    • 2.配合feign
  • seata优缺点

seata执行过程

SpringBoot+Seata在多数据源和feign中的简单使用_第1张图片
(官网的图)

安装seata

seata官网安装教程

下载seata

我选择了用docker下载选择1.6.1最新的版本,docker-compose启动,有个注意的细节,官网说明了在这里插入图片描述
SpringBoot+Seata在多数据源和feign中的简单使用_第2张图片

docker pull seataio/seata-server:1.6.1

在这里插入图片描述

使用自定义配置文件,NACOS为注册中心

先启动一次把application.yml配置文件放在宿主机中,(注意1.4.2之前是file.conf,配置写法也不相同)

docker run -d -p 8091:8091 -p 7091:7091  --name seata-serve seataio/seata-server:1.6.1
docker cp seata-serve:/seata-server/resources /home/seata/config

拷出后可以,可以选择修改application.yml再cp进容器,或者rm临时容器,如下重新创建,并做好映射路径设置,我选择了删除旧镜像使用docker-compose启动新的镜像。
找到/home/seata/config/resources/application.yml,修改对应的配置,详细配置文件里面有个 application.example.yml可以参考或者上官网查看

SpringBoot+Seata在多数据源和feign中的简单使用_第3张图片

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
    nacos:
      server-addr: nacosIP:8848 #nacos地址
      namespace: seata #nacos命名空间
      group: SEATA_GROUP #组名
      usernam: nacos #账号
      password: xxx #密码
      data-id: seataServer.properties  #读取的配置文件,官网有

  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: nacosIP:8848
      group: SEATA_GROUP
      namespace: seata
      # tc集群名称
      cluster: default
      username: nacos
      password: xxx #密码
    #  store:
    # support: file 、 db 、 redis
  #    mode: file
  #  server:
  #    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

dcoker-compose

version: "3"
services:
  seata-server:
    image: seataio/seata-server:1.6.1 #镜像
    container_name: my-seata  #容器名称
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      #一定要指定store_mode,因为application.yml没指定,如application.yml指定了这可以不指定。如无指定会出现全局数据开启失败的问题
      - STORE_MODE=db  #指定为数据库,配置文件写在nacos
      # 以SEATA_IP作为host注册seata server
      - SEATA_IP=xx.xx.xx.xx  #该IP一定是要服务能访问到的地址
      - SEATA_PORT=8091   #服务端口           
    volumes:
      - "/home/seata/config/resources:/seata-server/resources"
      - "/usr/share/zoneinfo/Asia/Shanghai:/etc/localtime"        #设置系统时区
      - "/usr/share/zoneinfo/Asia/Shanghai:/etc/timezone"  #设置时区


nacos上的配置文件seataServer.properties,配置文件官方有官方配置

SpringBoot+Seata在多数据源和feign中的简单使用_第4张图片

store.mode=db
#-----db-----
store.db.datasource=druid 
store.db.dbType=mysql #指定数据库类型
# 需要根据mysql的版本调整driverClassName  
# mysql8及以上版本对应的driver:com.mysql.cj.jdbc.Driver
# mysql8以下版本的driver:com.mysql.jdbc.Driver
store.db.driverClassName=com.mysql.cj.jdbc.Driver #在数据库执行select version()查看版本;
store.db.url=jdbc:mysql://xx.xx.xx.xx:3306/seata-server?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
store.db.user= root
store.db.password=root
# 数据库初始连接数
store.db.minConn=1
# 数据库最大连接数
store.db.maxConn=20
# 获取连接时最大等待时间 默认5000,单位毫秒
store.db.maxWait=5000
# 全局事务表名 默认global_table
store.db.globalTable=global_table
# 分支事务表名 默认branch_table
store.db.branchTable=branch_table
# 全局锁表名 默认lock_table
store.db.lockTable=lock_table
# 查询全局事务一次的最大条数 默认100
store.db.queryLimit=100


# undo保留天数 默认7天,log_status=1(附录3)和未正常清理的undo
server.undo.logSaveDays=7
# undo清理线程间隔时间 默认86400000,单位毫秒
server.undo.logDeletePeriod=86400000
# 二阶段提交重试超时时长 单位ms,s,m,h,d,对应毫秒,秒,分,小时,天,默认毫秒。默认值-1表示无限重试
# 公式: timeout>=now-globalTransactionBeginTime,true表示超时则不再重试
# 注: 达到超时时间后将不会做任何重试,有数据不一致风险,除非业务自行可校准数据,否者慎用
server.maxCommitRetryTimeout=-1
# 二阶段回滚重试超时时长
server.maxRollbackRetryTimeout=-1
# 二阶段提交未完成状态全局事务重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.committingRetryPeriod=1000
# 二阶段异步提交状态重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.asynCommittingRetryPeriod=1000
# 二阶段回滚状态重试回滚线程间隔时间  默认1000,单位毫秒
server.recovery.rollbackingRetryPeriod=1000
# 超时状态检测重试线程间隔时间 默认1000,单位毫秒,检测出超时将全局事务置入回滚会话管理器
server.recovery.timeoutRetryPeriod=1000

在seata使用的数据库上建立这几张表,官方有sql

SpringBoot+Seata在多数据源和feign中的简单使用_第5张图片

CREATE TABLE `branch_table` (
  `branch_id` bigint NOT NULL,
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint DEFAULT NULL,
  `resource_group_id` varchar(32) DEFAULT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `branch_type` varchar(8) DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  `client_id` varchar(64) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime(6) DEFAULT NULL,
  `gmt_modified` datetime(6) DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `distributed_lock` (
  `lock_key` char(20) NOT NULL,
  `lock_value` varchar(20) NOT NULL,
  `expire` bigint DEFAULT NULL,
  PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `global_table` (
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint DEFAULT NULL,
  `status` tinyint NOT NULL,
  `application_id` varchar(32) DEFAULT NULL,
  `transaction_service_group` varchar(32) DEFAULT NULL,
  `transaction_name` varchar(128) DEFAULT NULL,
  `timeout` int DEFAULT NULL,
  `begin_time` bigint DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `lock_table` (
  `row_key` varchar(128) NOT NULL,
  `xid` varchar(128) DEFAULT NULL,
  `transaction_id` bigint DEFAULT NULL,
  `branch_id` bigint NOT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `table_name` varchar(32) DEFAULT NULL,
  `pk` varchar(36) DEFAULT NULL,
  `status` tinyint NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`),
  KEY `idx_status` (`status`),
  KEY `idx_branch_id` (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

AT模式还要在对应的业务库加上undo_log表

CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int 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=26 DEFAULT CHARSET=utf8mb3;

docker-compose up -d 后台启动seata
docker logs -f seata-server 查看执行日志

nacos上看到seata服务
SpringBoot+Seata在多数据源和feign中的简单使用_第6张图片

IP:7091能登陆seata管理界面
SpringBoot+Seata在多数据源和feign中的简单使用_第7张图片

结合springboot实现AT模式


1.多数据源

引入依赖

        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>dynamic-datasource-spring-boot-starterartifactId>
        dependency>
        
        <dependency>
            <groupId>io.seatagroupId>
            <artifactId>seata-spring-boot-starterartifactId>
            <version>1.5.2version>
        dependency>

bootstrap.yml配置

seata:
  enabled: true #开启
  enable-auto-data-source-proxy: false  #关闭数据源代理,这里不关闭会出现事务回滚问题,具体可以查看
  application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name}
  tx-service-group: mytest-group # Seata 事务组编号,用于 TC 集群名
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: xx.xx.xx.xx:8848
      namespace: 332a6313-c96c-4c5d-b3b5-3063093bf8f9
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: xx.xx.xx.xx:8848
      namespace: 332a6313-c96c-4c5d-b3b5-3063093bf8f9
      group: SEATA_GROUP
  # 服务配置项,对应 ServiceProperties 类
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      multi-datasource-service-group: default
    # 分组和 Seata 服务的映射
    grouplist:
      default: xx.xx.xx.xx:8091 #seata服务地址

SpringBoot+Seata在多数据源和feign中的简单使用_第8张图片
(图片来源网络)

在使用的方法上用@GlobalTransactional注解

SpringBoot+Seata在多数据源和feign中的简单使用_第9张图片
SpringBoot+Seata在多数据源和feign中的简单使用_第10张图片

调用接口正常时

在这里插入图片描述
SpringBoot+Seata在多数据源和feign中的简单使用_第11张图片
数据能正常拆入2个表中
SpringBoot+Seata在多数据源和feign中的简单使用_第12张图片

调用接口报错时回滚

调用前,远程服务器的remotest表无数据

SpringBoot+Seata在多数据源和feign中的简单使用_第13张图片

执行全局事务,TM申请了XID

SpringBoot+Seata在多数据源和feign中的简单使用_第14张图片
SpringBoot+Seata在多数据源和feign中的简单使用_第15张图片

执行远程数据插入,数据表出现数据的remotest表会删除刚插入的数据

SpringBoot+Seata在多数据源和feign中的简单使用_第16张图片
本地数据库主键冲突报错,会执行全局事务的回滚,发现远程数据库
SpringBoot+Seata在多数据源和feign中的简单使用_第17张图片


2.配合feign

开启2个服务注册到nacos上

SpringBoot+Seata在多数据源和feign中的简单使用_第18张图片

调用方法,TM申请到了XID

SpringBoot+Seata在多数据源和feign中的简单使用_第19张图片

远程服务获取到了相同的xid

SpringBoot+Seata在多数据源和feign中的简单使用_第20张图片

执行完数据操作后,该服务RM会告诉TC执行完成

SpringBoot+Seata在多数据源和feign中的简单使用_第21张图片

本地方法报错RM会告诉TC执行回滚,远程数据的RM也会被告知进行回滚

SpringBoot+Seata在多数据源和feign中的简单使用_第22张图片

feign把xid添加到head中

    String xid = RootContext.getXID();
    requestTemplate.header(RootContext.KEY_XID, xid);
public class FeignInterceptorConfig implements RequestInterceptor  {

    Logger log  = LoggerFactory.getLogger(FeignInterceptorConfig.class);

    public void apply(RequestTemplate requestTemplate) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        if (ObjectUtils.isEmpty(requestAttributes)){
            return ;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        //可以在这里将自定义请求头传递进去, key 请求, value 值
        //处理上游请求头信息,传递时继续携带
       
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                // 跳过 content-length
                if ("content-length".equals(name)) {
                    continue;
                }
                requestTemplate.header(name, values);
            }
           //传递 seata 的 xid
           String xid = RootContext.getXID();
           requestTemplate.header(RootContext.KEY_XID, xid);
        }
        else {
            log.info("feign interceptor error header:{}", requestTemplate);
        }
    }
}

如果开启了hystrix会导致添加的head失效,因为获取的ServletRequestAttributes是null,要修改hystrix的策略。
参考下文
hystrix开启导致feign添加head失效

feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          # 隔离策略
          strategy: SEMAPHORE  
          thread:
            timeoutInMilliseconds: 100000

seata优缺点

优点:
1.业务侵入性低
2.TC(事务协调者)服务端能单独部署,利用nacos等注册中心能起多个

SpringBoot+Seata在多数据源和feign中的简单使用_第23张图片
(图片截取于网络)

缺点:

SpringBoot+Seata在多数据源和feign中的简单使用_第24张图片
(图片截取于网络)

你可能感兴趣的:(java,spring,boot,java,spring)