【Middleware】Seata部署及使用

官网

github:https://github.com/seata/seata

github-example:https://github.com/seata/seata-samples

offical-website:https://seata.io/

简介

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

PS: Seata是比较难以理解的一个框架,强烈建议在学习Seata框架时,先认真阅读官网内容,以了解整个Seata各种概念和设计思路,再动手写代码。

脚本方式安装

1. 准备

安装JDK

下载应用包

应用包名称 下载地址
seata-server-1.4.2.tar.gz https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.tar.gz
seata-server.sql https://github.com/seata/seata/blob/1.4.2/script/server/db/mysql.sql

上传应用包

1
scp seata-server-1.4.2.tar.gz root@xxx:/usr/local/src/

解压应用包

1
tar -zxvf seata-server-1.4.2.tar.gz

移动到安装目录(/usr/local/seata)

1
mv /usr/local/src/seata/seata-server-1.4.2/ /usr/local/seata-server

2. 创建数据库

1
2
3
# MySQL命令行中
create database seata_server_db;
source seata-server.sql

3. 配置Seata信息

  • seata支持通过file、nacos、eureka、redis、zk、consul、etcd3、sofa方式注册服务
  • seata支持使用file、nacos、apollo、zk、consul、etcd3作为配置中心

以服务注册方式:file,服务配置方式:file,数据存储方式:file

不需要改动任何配置文件,当这种方式不可实现高可用部署,高可用部署架构上数据存储方式不能使用file方式。

以服务注册方式:eureka,服务配置方式:apollo,数据存储方式:mysql

  • Apollo中配置Seata属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    store.mode = db
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    store.db.datasource = druid
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    store.db.dbType = mysql
    store.db.driverClassName = com.mysql.jdbc.Driver
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    store.db.url = jdbc:mysql://xxx:xxx/xxx_db?rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false
    store.db.user = xxx
    store.db.password = xxxx
    store.db.minConn = 5
    store.db.maxConn = 30
    store.db.globalTable = global_table
    store.db.branchTable = branch_table
    store.db.lockTable = lock_table
    store.db.queryLimit = 100
    store.db.maxWait = 5000
  • 配置registry.conf文件
    1
    2
    # 修改registry.conf
    vi /usr/local/seata-server/conf/registry.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 修改registry下的eureka模块
    eureka {
    serviceUrl = "<eureka的服务地址>"
    application = "<seata在eureka中的服务名,建议seata-server>"
    weight = "1"
    }
    # 修改config下的apollo模块
    apollo {
    appId = "<Apollo已配置项目名称>"
    ## apolloConfigService will cover apolloMeta
    apolloMeta = "<Apollo-Meta的服务地址>"
    apolloConfigService = "<Apollo-Config的服务地址,一般和Apollo-Meta服务的一致>"
    namespace = "<Apollo中配置的Seata命名空间>"
    apolloAccesskeySecret = "<若有,则配置>"
    cluster = "<Apollo配置的集群名称>"
    }

4. 启动服务

1
2
# "-p"表示指定服务端口
nohup sh /usr/local/seata-server/bin/seata-server.sh -p <端口号> > /usr/local/seata-server/logs/seata-server.log 2>&1 &

5. 查看日志

查看文件/usr/local/seata-server/conf/logback/file-appender.xml内容可知,默认的日志路径为${user.home}/logs/seata

1
2
# 或tail -f /usr/local/seata-server/logs/seata-server.log
tail -f ~/logs/seata/seata-server.<端口号>.all.log

6. 停止服务

1
kill -9 `ps -aux | grep seata | grep -v grep | awk '{print $2}'`

Kubernetes方式安装

1. 创建数据库

和脚本方式安装中的创建数据库步骤一致

2. 部署服务(以服务注册方式:eureka,服务配置方式:apollo,数据存储方式:mysql进行部署)

修改其中<namespace-name>的内容,以及配置文件相关属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
---
# configmap for seata-server
kind: ConfigMap
apiVersion: v1
metadata:
name: configmap-seata-server
namespace: <namespace-name>
data:
registry.conf: |
registry {
type = "eureka"
eureka {
serviceUrl = "<eureka的服务地址>"
application = "<seata在eureka中的服务名,建议seata-server>"
weight = "1"
}
}
config {
type = "apollo"
apollo {
appId = "<Apollo已配置项目名称>"
## apolloConfigService will cover apolloMeta
apolloMeta = "<Apollo-Meta的服务地址>"
apolloConfigService = "<Apollo-Config的服务地址,一般和Apollo-Meta服务的一致>"
namespace = "<Apollo中配置的Seata命名空间>"
cluster = "<Apollo配置的集群名称>"
}
}
---
# deployment for seata-server
kind: Deployment
apiVersion: apps/v1
metadata:
name: deployment-seata-server
namespace: <namespace-name>
labels:
app: seata-server
spec:
replicas: 3
selector:
matchLabels:
app: seata-server
template:
metadata:
labels:
app: seata-server
spec:
# 存储资源定义
volumes:
- name: volume-configmap-seata-server
configMap:
name: configmap-seata-server
defaultMode: 420
containers:
- name: container-seata-server
image: 'seataio/seata-server:latest'
ports:
- name: http
containerPort: 8091
protocol: TCP
# 环境变量
env:
- name: SEATA_CONFIG_NAME
value: 'file:/root/seata-config/registry'
resources:
limits:
cpu: '1'
memory: 2000Mi
requests:
cpu: 500m
memory: 1000Mi
# 存储资源挂载
volumeMounts:
- name: volume-configmap-seata-server
mountPath: /root/seata-config
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1

SpringBoot中使用

1. 创建undo_log表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 注意此处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;

2. 引入依赖

注意版本强烈建议和部署的服务版本一致

1
2
3
4
5
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata.version}</version>
</dependency>

3. 修改SpringBoot配置文件(application.yml或application.properties)

Seata独立部署方式配置

1
2
3
4
seata.enabled = true
seata.service.grouplist.seata-server = <seata-server的地址(含端口)>
seata.service.vgroup-mapping.tx_service_group_xxx = seata-server
seata.tx-service-group = tx_service_group_xxx

Seata基于Eureka服务部署方式配置

1
2
3
4
5
seata.enabled = true
seata.registry.type = eureka
seata.registry.eureka.service-url = <Eureka地址,多个逗号(,)隔开>
seata.service.vgroup-mapping.tx_service_group_xxx = seata-server
seata.tx-service-group = tx_service_group_xxx

4. 与ORM框架整合(配置数据源代理)

seata虽说是和主流的ORM框架都能支持整合,但整合过程还是需要进行一定的配置的,其中最重要的就是数据源代理了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Value("${seata.enabled:false}")
private boolean isSeataEnabled;

@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}

@Primary
@Bean("dataSource")
public DataSource dataSource(DataSource druidDataSource) {
if (isSeataEnabled) {
return new DataSourceProxy(druidDataSource);
}
else {
return druidDataSource;
}
}

5. 与RPC框架整合(配置事务传播)

seata的全局事务是基于内部的XID确定全局唯一事务ID的,而下游服务自动默认是从Request-Header中获取的,所以基于不同的RPC框架,有不同的事务传播配置。
官网的微服务框架支持 https://seata.io/zh-cn/docs/user/microservice.html 文章中有具体的说明。
当前用的RPC框架为Feign,所以以Feign为例,创建Feign的请求拦截器类,把当前的XID放到请求头里:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@ConditionalOnClass({RequestInterceptor.class, RootContext.class})
public class FeignRequestSeataInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 传输Seata的xid到下游服务
String xid = RootContext.getXID();
if (StrUtil.isNotBlank(xid)) {
template.header(RootContext.KEY_XID, xid);
}
}
}

6. 使用注解@GlobalTransactional启用Seata分布式事务

1
2
3
4
5
6
7
@Server
public class UserServiceImpl {
@GlobalTransactional
public Result<String> save(User user) {
//TODO: 具体业务逻辑
}
}

注意事项

1. 脚本方式单机部署的高可用或多实例

如果想在同一台服务器部署多个实例,不能单单改了端口执行同一条命令启动服务命令,这样会由于部分文件在不同实例下共同使用而出错,这一点已经测试证实过了,至少使用Seata1.2.0和Seata1.3.0版本都测试过。

所以有多少个实例,就要创建多少个Seata部署文件夹,每个文件夹各自管理各自的实例。如部署例子分别创建/usr/local/seata1//usr/local/seata2//usr/local/seata3/三个目录,并把seata的解压内容复制到目录内,分别用不同端口执行不同目录下的bin/seata-server.sh脚本,这样才能正确地得到三个seata实例。

(•̀ᴗ•́)و ̑̑

Share