Seata分布式事务部署与使用(集成Nacos)

这几天在使用微服务进行开发,搭建架构时需要集成Seata分布式事务管理,中间遇到了一些坑,决定写这篇文章以后集成Seata时可以快速解决问题

1.Seata简介

1.1Seata是什么

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各BU业务进行了有力的支撑。经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备。

1.2AT模式

  • 提供无侵入自动补偿的事务模式,目前已支持 MySQL、 Oracle 、PostgreSQL和 TiDB的AT模式,H2 开发中

1.3TCC模式

  • 支持 TCC 模式并可与 AT 混用,灵活度更高

1.4SAGA模式

  • 为长事务提供有效的解决方案

1.5XA模式

  • 似乎正在研发中
  • 总而言之,大部分情况下我们需要了解的是AT模式

2.Seata的下载与运行

2.1下载

  • 直接官网下载:https://seata.io/zh-cn/index.html
  • windows下载zip包,linux下载tar.gz即可,我使用的是1.4.0版本的

2.2运行

  • 将下载好的压缩包解压
  • 解压后直接执行bin下面的seata-server.sh(windows双击seata-server.bat)即可运行seata,默认端口是8091

3.Nacos集成

3.1添加Seata配置到Nacos中

  • 基本配置配置如下,具体配置请查阅官方文档
#这里的demo-system-group表示当前的组,用户可以自己定义组名如lat-system-group也可,但是在后面配置文件中必须对应,见5.注意点详解
service.vgroupMapping.demo-system-group=default
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/ry-seata?useUnicode=true
store.db.user=root
store.db.password=password
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
#下面这条配置修改了写日志时序列化数据的方式,如果不修改序列化方式可能出现意外的错误,见5.注意点
client.undo.logSerialization=kryo
  • 注意这里的配置每行都对应一个Data Id,配置完成后Nacos配置文件应如下图(部分配置)
    在这里插入图片描述

3.2将Seata注册到Nacos中

  • 打开解压后的压缩包在config文件夹下,编辑修改registry.conf文件,本人如下(注册中心和配置中心都选择nacos)
registry {
  # file 注册信息在指定文件下  
  # nacos 注册到nacos注册中心  
  # eureka注册到eureka注册中心 
  # redis 注册到redis作为注册中心 
  # zk 注册到zk注册中心
  # consul注册到consul注册中心 
  # etcd3 注册到etcd3注册中心 
  # sofa 注册到sofa注册中心
  type = "nacos"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10

  nacos {
    application = "seata-server"
    serverAddr = "192.168.100.201:8849"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = 0
    password = ""
    cluster = "default"
    timeout = 0
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # 同上,选择一个配置中心
  type = "nacos"

  nacos {
    serverAddr = "192.168.100.201:8849"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
    apolloAccesskeySecret = ""
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

3.3添加Seata数据库

  • 微服务大多是分库的,这里给Seata一个独立的数据库方便Seata持久化,建表语句如下
-- -------------------------------- 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 = utf8mb4;

-- 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 = utf8mb4;

-- 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 = utf8mb4;

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(100) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
  • 必须注意的是undo_log表,这张表需要在每个需要用到全局事务的业务库中都有一份,每个事务执行过程中都会向当前数据源的这张表写入数据

3.4重启Seata,Seata服务被注册到Nacos服务列表中

在这里插入图片描述

4.使用Seata做事务管理

4.1添加依赖

  • 这里直接集成SpringCloud-Alibaba的启动器就可以,本人用的2021.1版本,注意是所有需要全局事务管理的微服务都需要添加该依赖,建议在父工程中进行统一版本管理
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

4.2修改业务配置文件

  • 在配置文件中进行多数据源动态配置的时候需要开启Seata配置开关(必须),另外需要配置Seata参数,本人样例配置如下
# spring配置
spring: 
  datasource:
    druid:
      stat-view-servlet:
        enabled: true
        loginUsername: admin
        loginPassword: 123456
    dynamic:
      druid:
        initial-size: 5
        min-idle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,slf4j
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      datasource:
          # 主库数据源
          master:
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://192.168.100.201:3306/demo-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
            username: ***
            password: ***
          # 从库数据源
          # slave:
            # username: 
            # password: 
            # url: 
            # driver-class-name: 
      seata: true    # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭

# seata配置
seata:
  # 默认关闭,如需启用spring.datasource.dynami.seata需要同时开启
  enabled: true
  # Seata 应用编号,默认为 ${spring.application.name}
  application-id: ${spring.application.name}
  # Seata 事务组编号,用于 TC 集群名,需要注意与前面Nacos配置一致,否则无法拉取到Seata服务,见5.注意点
  tx-service-group: demo-system-group
  # 关闭自动代理
  enable-auto-data-source-proxy: false
  # 服务配置项
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      ruoyi-system-group: default
  config:
    type: nacos
    nacos:
      serverAddr: 192.168.100.201:8849
      group: SEATA_GROUP
      namespace:
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.100.201:8849
      namespace:

4.3开启事务注解

  • 每个事务的开始使用@GlobalTransactional注解开启全局事务
  • 每个分支事务使用@Transaction注解开启当前服务事务
  • 每个多数据源的服务必须指定当前当前数据源

5.注意点

5.1 业务服务启动报错,连接不上服务 服务报错 no available service ‘null‘ found, please make sure registry config correct

  • 错误关键字
no available service ‘null‘ found, please make sure registry config correct
  • 原因:配置seata分组与业务中指定分组不一致,找不到指定分组的seata服务
  • 解决方法:Nacos配置中的service.vgroupMapping.demo-system-group,其最后一段(demo-system-group)与业务服务配置的seata.service.vgroup.demo-system-group的最后一段(demo-system-group)必须对应

5.2 nacos集成分布式事务插件Seata的序列化问题

  • 错误关键字
json encode exception, Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer)
  • 实际上是Seata本身存在bug
  • 解决方法:在Nacos配置列表中添加client.undo.logSerialization=kryo

5.3获取Seata服务失败

  • 错误关键字
no available service 'default' found, please make sure registry config correct
  • 其他服务拉取Seata事务控制服务失败
  • 检查Seata是否启动成功,配置文件是否配置正确,如5.1的问题

5.4Seata后台运行

  • cd到Seata的bin文件夹下
  • nohup ./seata-server.sh 2>1 &
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,关于这个问题,我可以提供一些参考资料。首先,Spring Cloud是一个基于Spring框架的微服务架构开发工具包,提供丰富的开箱即用的组件和框架,可以较为方便地实现微服务的开发、部署和管理。而Nacos是阿里巴巴开源的一个面向服务中心的动态服务发现、配置管理和服务治理平台,可以提供服务注册与发现、配置管理、流量管理、域名解析等功能。Seata是阿里巴巴开源的一个分布式事务解决方案,可以提供数据源代理、事务协调、幂等性设计等功能,用于解决微服务架构下的分布式事务问题。 在Spring Cloud集成NacosSeata,需要进行如下步骤: 1. 引入相应的依赖,例如在pom.xml文件中添加如下依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.3.RELEASE</version> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> ``` 其中,spring-cloud-starter-alibaba-nacos-config和spring-cloud-starter-alibaba-nacos-discovery是Nacos的客户端依赖,seata-spring-boot-starter是Seata的客户端依赖。 2. 配置Nacos的地址和Seata的事务配置,例如在application.yml文件中添加如下配置: ```yaml spring: cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yml seata: tx-service-group: my_test_tx_group application-id: ${spring.application.name} enabled: true mybatis: configuration: # ... seata: enabled: true application-id: ${spring.application.name} tx-service-group: my_test_tx_group config: transport: enabled: true type: TCP server: localhost:8091 heartbeat: true heartbeat.interval.ms: 5000 disableHeartbeatChecking: true client-selector: rnd client-list: 127.0.0.1:8091 shutdown.timeout.ms: 5000 registry: type: Nacos nacos: application: ${spring.application.name} serverAddr: localhost:8848 namespace: public ``` 其中,Nacos的地址和Seata的事务配置可以根据具体情况自行修改。 3. 配置完毕后,就可以使用Nacos作为Spring Cloud的注册中心和配置中心,使用Seata来解决分布式事务问题了。 以上是关于如何在Spring Cloud集成NacosSeata的简要介绍,希望能够帮助到你。如果你还有其他问题,可以继续向我提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值