版本信息
组件 | 版本 | 说明 |
---|---|---|
SpringBoot | 2.1.3.RELEASE | |
SpringCloud | Greenwich.SR6 | |
SpringCloudAlibaba | 2.1.4.RELEASE | |
Nacos | 1.4.1 | |
Seata | 1.4.0 |
部署Seata服务端(https://github.com/seata/seata/releases)
- 下载Seata服务端 https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.tar.gz
- 解压tar包,进入conf目录,编辑== registry.conf ==(Seata配置注册中心和属性信息的文件)文件。使用nacos注册中心,配置中心暂时使用file,测试正常以后换为nacos
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "域名|IP:端口"
group = "SEATA_GROUP"
namespace = "dev"
cluster = "default"
username = ""
password = ""
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
nacos {
serverAddr = "域名|IP:端口"
namespace = "dev"
group = "SEATA_GROUP"
username = ""
password = ""
dataId = "seataServer.properties"
}
file {
name = "file.conf"
}
}
- 进入bin目录,启动Seata服务端
# -h 注册到nacos后的ip
# -p 服务端口
# -m 启动模式(暂时使用文件模式),运行正常后改为数据库模式
./seata-server.sh -h 102.47.15.128 -p 9000 -m file -n 1
- 修改配置中心为Nacos。
- 下载https://github.com/seata/seata/archive/refs/tags/v1.4.2.zip 源码
- 复制seata-1.4.2\script\config-center\config.txt文件到服务器seata-server-1.4.2目录下
- 复制seata-1.4.2\script\config-center\nacos\nacos-config.sh文件到seata-server-1.4.2\conf目录下
- 修改config.txt文件,修改存储方式(store.mode),也可同步到nacos上以后再去改。(启动seata-server时指定 -m db 为使用db存储模式,或者修改config中store.mode=file为store.mode=db,或同步到nacos以后再nacos中修改)
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=username
store.db.password=password
-
- 同步配置信息到nacos
#-h nacos域名|IP
#-p nacos端口
#-g 配置信息分组
#-t 配置信息命名空间
./nacos-config.sh -h 123.123.123.123 -p 80 -g SEATA_GROUP -t dev
-
- 重新启动seata(如果-m db 模式时一定要修改数据库信息),seata数据库sql如下:
-- -------------------------------- 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 = utf8;
-- 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 = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`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 = utf8;
springboot 整合seata
- maven依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.version}</version>
</dependency>
- seata配置
seata:
tx-service-group: ${spring.application.name}
application-id: ${spring.application.name}
registry:
type: nacos
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group: SEATA_GROUP
注意:seata.tx-service-group 对应的值一定要再nacos中配置以后才可以使用。如seata.tx-service-group=user;配置如下
- 使用AT代理数据库
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean
public DataSourceProxy dataSource(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
- 全局事务入口添加注解:@GlobalTransactional(回开启全局事务,注册全局事务XID,并由SeataHandlerInterceptor类将XID设置再http请求头中传递),即可实现分布式事务
注意:分布式事务链路中的个个分支事务异常得向上抛,才能被事务发起者感知到,然后触发回滚。或自己根据业务判断是否异常,主动触发回滚(分支事务也可主动触发回滚)。如下:
String xid = RootContext.getXID();
if(StringUtils.isNotBlank(xid)){
GlobalTransactionContext.reload(xid).rollback();
}
因为我的项目中都是 Controler接口互相调用,也设置了全局异常处理,所以不会自动异常回滚。处理方式为在异常处理中主动触发回滚。如下:
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理其他异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultVo exceptionHandler(HttpServletRequest req, Exception e){
try{
//主动回滚
String xid = RootContext.getXID();
if(StringUtils.isNotBlank(xid)){
GlobalTransactionContext.reload(xid).rollback();
}
}catch (Exception exception){
exception.printStackTrace();
}
String msg = e.getMessage();
if(e instanceof NullPointerException){
msg = "NULL_POINTER_EXCEPTION";
}
e.printStackTrace();
return ResultVo.fail(msg );
}
}