SEATA 配置
使用 nacos 做为配置中心配置 SEATA
当前 SEATA 版本: 1.4.1
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
配置参数
nacos bash 脚本
同步 config 配置到 nacos
进入 TC 服务器
新建文件夹 seata-config
进入 seata-config
新建 config.txt 文件并复制配置参数到 config.txt 文件中
新建 nacos-config.sh 文件,同时复制 nacos bash 脚本到 nacos-config.sh 中
使用以下命令同步配置参数到 nacos
bash nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 3a2aea46-07c6-4e21-9a1e-8946cde9e2b3 -u nacos -w nacos
得到输出
set nacosAddr=127.0.0.1:8848 set group=SEATA_GROUP Set transport.type=TCP successfully Set transport.server=NIO successfully . . . ========================================================================= Complete initialization parameters, total-count:80 , failure-count:0 ========================================================================= Init nacos config finished, please start seata-server.
使用 docker 部署 SEATA
Docker 部署 SEATA 官方文档
进入 TC 服务器,并进入 seat-config 文件夹
新建 registry.conf 文件,并添加以下内容,registry 配置参考
新建 file.conf 文件并添加以下内容(可选,可通过 nacos 读取)
运行 docker 命令
- 注意: 当在 config.txt 中配置 store.mode=db 时,需要在配置的数据库连接中初始化表
global_table
、branch_table
、lock_table
,sql 传送门。
docker run -d --name seata-server \ --net=host \ -p 8091:8091 \ -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \ -v /root/seata-config:/root/seata-config \ seataio/seata-server:1.4.1
挂载目录为 TC 服务器配置目录。
- 注意: 当在 config.txt 中配置 store.mode=db 时,需要在配置的数据库连接中初始化表
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
例子:业务聚合服务
SEATA 包引入。pom 配置如下,当使用
spring-cloud-starter-openfeign
包时,需要移除spring-cloud-starter-openfeign
包,spring-cloud-starter-alibaba-seata
中已经包含了spring-cloud-starter-openfeign
,再次引入可能导致包冲突。
添加 registry.conf。在工程中 resource 目录下添加如下内容
配置 bootstrap.properties,添加配置,内容如下,
seata.tx-service-group
和namespace
改为对应的值
在 TM 中通过 @GlobalTransactional
开启全局异常,示例代码:
@GlobalTransactional
@GetMapping({"create"})
public String create(String name,Integer age) {
...
return "创建成功";
}
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
例子:被调用服务。
配置 bootstrap.properties,添加配置,内容如下,
seata.tx-service-group
和namespace
改为对应的值对需要做回滚的业务标记
@Transactional(rollbackFor = Exception.class)
如果配置了全局异常处理,使用 SEATA API 发起事务回滚
@ExceptionHandler(value = Exception.class) @ResponseBody public String exceptionHandler(Exception e) { ... try { String xid = RootContext.getXID(); if (StringUtils.isNotEmpty(xid)) { GlobalTransactionContext.reload(RootContext.getXID()).rollback(); } } catch (TransactionException transactionException) { transactionException.printStackTrace(); log.error("===TransactionException==={}", transactionException.getMessage()); } return e.getMessage(); }
或者通过 AOP 全局处理回滚
/** * @author Zhang_Xiang * @since 2021/2/22 17:36:16 */ @Aspect @Component @Slf4j public class TxAspect { @Pointcut("execution(public * *(..))") public void publicMethod() { } @Pointcut("within(com.*.service.impl..*)") private void services() { } @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") private void transactional() { } @Pointcut("within(com.*.webapi.controller..*)") private void actions() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.ExceptionHandler))") private void validatedException() { } @Before(value = "validatedException()") public void beforeValidate(JoinPoint joinPoint) throws TransactionException { Object[] args = joinPoint.getArgs(); if (args == null || args.length == 0) { return; } Exception e = (Exception) args[0]; if (e instanceof MethodArgumentNotValidException || e instanceof BindException || e instanceof ConstraintViolationException) { globalRollback(); } } @AfterThrowing(throwing = "e", pointcut = "publicMethod()&&services()&&transactional()") public void doRecoveryMethods(Throwable e) throws TransactionException { log.info("===method throw===:{}", e.getMessage()); globalRollback(); } @AfterReturning(value = "publicMethod()&&actions()", returning = "result") public void afterReturning(RestResponse<?> result) throws TransactionException { log.info("===method finished===:{}", result); if (result.isFail()) { globalRollback(); } } //region private methods private void globalRollback() throws TransactionException { if (!StringUtils.isBlank(RootContext.getXID())) { log.info("===xid===:{}", RootContext.getXID()); GlobalTransactionContext.reload(RootContext.getXID()).rollback(); } } //endregion }
AT(Automatic Transaction) 模式下配置 undo_log 数据库表,mysql 建表 sql 如下
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 = utf8 COMMENT ='AT transaction mode undo table';
关闭 SEATA
seata.enabled=false