Spring Cloud 分布式事务 Seata

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

阅读本篇文章您将了解到如下内容:

  • Seata
  • Seata Server 部署
  • Seata 事务分组

如果您了解以下内容, 可便于理解:

本文为应用型文章, 将不会阐述过多理论知识

术语解析

本次部署中会用到以下术语:

  • Seata Server: Seata服务端, 官方称之为事务协调者TC(Transaction Coordinator), 单独部署
  • Seata Client: Seata客户端, 官方称之为资源管理器RM(Resource Manager), 与业务系统集成

官方术语表: Seata术语

技术选型

Seata有多种部署方案, 生产环境推荐使用如下方式: 注册中心+配置中心+数据库

对应的技术栈如下:

  • 注册中心: Nacos
  • 配置中心: Nacos
  • 数据库: MySQL 8.0

部署Seata Server

初始化数据库

创建数据库seata-server及对应的访问用户seata

使用官方的数据库脚本进行初始化: seata/script/server/db at develop

除MySQL以外还支持Oracle和PostgreSQL

自定义配置文件

参照官方部署文档: 使用 Docker compose 快速部署 Seata Server

编写服务端配置文件registry.conf:

registry {
  type = "nacos"
  
  nacos {
    # 服务名称
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    # 命名空间ID
    namespace = "spring-cloud-development"
    group = "SEATA_GROUP"
    # 集群名称
    cluster = "default"
  }
}
​
config {
  type = "nacos"
  
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "spring-cloud-development"
    group = "SEATA_GROUP"
    # 配置文件dataId
    dataId: "seata-server.properties"
  }
}
复制代码

在nacos中增加配置seata-server.properties, 调整为数据库模式:

store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata-server?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
store.db.user=seata
store.db.password=seata
复制代码

注意: 配置的dataId和group应与registry.conf中的一致

使用docker-compose启动

对docker-compose不再赘述, 可阅读前言中的官方文档

编写docker-compose.yaml:

# 根据自己的docker版本选择对应的version
version: "3.1"
services:
  seata-server:
    image: seataio/seata-server:1.4.2
    hostname: seata-server
    ports:
      - "8091:8091"
    environment:
      - SEATA_PORT=8091
      - SEATA_IP=127.0.0.1
      # 此处有坑
      - SEATA_CONFIG_NAME=file:/root/seata-config/registry
    volumes:
      # 此处有坑
      - "./seata-server/config:/root/seata-config"
复制代码

这里有两个因为笔者不熟悉docker产生的坑:

环境变量SEATA_CONFIG_NAME, 指的是容器内部的环境

所以路径file:/root/seata-config/registry是指, 服务端读取的配置文件registry.conf为容器内部的/root/seata-config/registry.conf

并非我们本机的/root/seata-config/registry

所以我们要将自己写的配置文件映射到其内部中, 即后面一个坑: "./seata-server/config:/root/seata-config"

它的含义是将本机路径./seata-server/config映射到容器内部路径/root/seata-config

综上, 我们需要完成的操作是:

  1. docker-compose.yaml目录下创建seata-server/config目录
  2. registry.conf文件放置到${docker-compose.yaml目录}/seata-server/config
  3. 运行命令docker-compose up -d

如果您遇到了io.seata.common.exception.NotSupportYetException: config type can not be null, 请仔细阅读上述内容

业务系统集成Seata Client

初始化数据库

在业务系统中增加undo_log表:

-- 注意此处0.3.0+ 增加唯一索引 ux_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;
复制代码

自定义配置文件

在nacos中增加客户端公共配置文件seata-client.properties:

seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.namespace=spring-cloud-development
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.application=seata-server

seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.namespace=spring-cloud-development
seata.config.nacos.group=SEATA_GROUP
复制代码

注意: 客户端配置的group应与服务端配置相同

引入启动类依赖

按照Seata部署指南推荐的方式在pom.xml中引入依赖spring-cloud-starter-alibaba-seata:

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>最新版</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2.2.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
复制代码

之后再引入上一节中的客户端配置即可:

spring:
  cloud:
    nacos:
      config:
        shared-configs:
          - data-id: seata-client.properties
            group: SEATA_GROUP
复制代码

事务分组

想要学会一个概念, 最重要的是了解为什么要使用它

回顾最开始的概念, Seata Server是事务协调者TC(Transaction Coordinator)

也就是说它在分布式事务中起到至关重要的作用

但如何它挂了呢?或者网络波动了呢?

所以事务分组其实可以理解为Seata Server集群分组, 是为了TC的高可用

Seata Server配置

服务端要明确自己的分组名称, 修改registry.conf文件:

registry {
  
  nacos {
    # Seata Server所属集群名称为dev
    cluster = "dev"
  }
}
复制代码

按照官方文档Seata 事务分组, 此处有一个坑

需要在nacos中增加一个事务组名对应集群名的配置

以上面的集群为例, 需要增加service.vgroup-mapping.${分组名}配置, 内容为集群名称: dev

原因在于RegistryService中如下代码:

public interface RegistryService<T> {
​
    String PREFIX_SERVICE_MAPPING = "vgroupMapping.";
​
    String PREFIX_SERVICE_ROOT = "service";
​
    String CONFIG_SPLIT_CHAR = ".";
​
    default String getServiceGroup(String key) {
        // 假设分组名为group-dev
        // 会在nacos中寻找dataId为service.vgroupMapping.group-dev的配置
        key = PREFIX_SERVICE_ROOT + CONFIG_SPLIT_CHAR + PREFIX_SERVICE_MAPPING + key;
        if (!SERVICE_GROUP_NAME.contains(key)) {
            ConfigurationCache.addConfigListener(key);
            SERVICE_GROUP_NAME.add(key);
        }
        return ConfigurationFactory.getInstance().getConfig(key);
    }
}
复制代码

如果客户端启动后遇到can not get cluster name in registry config 'service.vgroupMapping.xx', please make sure registry config correct

请仔细阅读上述内容

Seata Client配置

在项目配置中增加如下内容即可:

seata:
  tx-service-group: group-dev
复制代码

最终效果为: 事务组group-dev会使用集群名称为dev的TC

应用场景

官方提供了很好的示例, 可参照: 事务分组与高可用 (seata.io)

总结

纸上得来终觉浅

理解Seata和实际部署Seata之间, 还隔着docker/nacos等技术鸿沟

最后总结下使用Seata的整体流程:

  1. 部署Seata Server
  2. 为事务进行分组
  3. 业务系统集成Seata Client

我是Houtaroy, 若有一行代码能为他人提供帮助, 便是在下的毕生荣幸

猜你喜欢

转载自juejin.im/post/7084113449579610119