一、Seata服務端搭建
1、下載服務端
官網(wǎng)下載:https://github.com/seata/seata/releases
下載1.5.2版本
2帜慢、創(chuàng)建Seata數(shù)據(jù)庫
CREATE DATABASE `ry-seata` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
3、創(chuàng)建Seata數(shù)據(jù)表
腳本在Seata版本解壓目錄/script/server/db下唯卖,目前支持mysql粱玲、oracle、postgresql拜轨;
庫表生成效果:
4抽减、創(chuàng)建Nacos配置
1)創(chuàng)建seata服務配置seataServer.properties;
Data ID:seataServer.properties
Group: SEATA_GROUP
NameSpace:seata
配置格式:properties
注:應用的Group分組名稱與Seata的分組名稱可以不一樣;
具體內(nèi)容:
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none
#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=192.168.2.93:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=kryo
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
#Log rule configuration, for client and server
log.exceptionRate=100
#Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional.
store.mode=db
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.2.93:3306/ry-seata?rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=60000
server.recovery.asynCommittingRetryPeriod=60000
server.recovery.rollbackingRetryPeriod=60000
server.recovery.timeoutRetryPeriod=60000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false
#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
2)創(chuàng)建事務路由規(guī)則配置service.vgroupMapping.default_tx_group
Data ID:service.vgroupMapping.default_tx_group
Group: SEATA_GROUP
NameSpace:seata
配置格式:TEXT
內(nèi)容:default
3)配置最終效果
5橄碾、修改seata/conf/application.yml文件內(nèi)容:
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${user.home}/logs/seata
# extend:
# logstash-appender:
# destination: 127.0.0.1:4560
# kafka-appender:
# bootstrap-servers: 127.0.0.1:9092
# topic: logback_to_logstash
console:
user:
username: seata
password: seata
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 192.168.10.164:8848
namespace: seata
group: SEATA_GROUP
username: nacos
password: nacos
file-extension: yml
data-id: seataServer.properties
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: 192.168.10.164:8848
group: SEATA_GROUP
namespace: seata
cluster: default
username: nacos
password: nacos
file-extension: yml
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
6卵沉、啟動Seata服務
windox環(huán)境颠锉,直接雙擊:seata/bin/seata-server.bat
linux環(huán)境:
sh seata-server.sh -p 8091 -h 192.168.10.164 -m db
注:務必加啟動參數(shù),特別是IP與端口
打開seata控制臺:
二史汗、Seata客戶端搭建
1琼掠、客戶端添加seata依賴
<!-- SpringBoot Seata -->
<!--移除alibaba-seata自帶的seata-spring-boot-starter,因為自帶的版本太低 -->
<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>
<!--添加seata-spring-boot-starter依賴停撞,版本需要與服務端版本相對應瓷蛙,此版本使用1.5.2 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
2、客戶端添加配置文件
注:seata客戶端的命名空間與分組名稱可以與seata服務端的不一樣戈毒,但若客戶端有多個配置文件艰猬,那它們的命名空間與分組名稱是要一致的;
1)創(chuàng)建seata客戶端的Nacos配置文件
Data ID:seata-client-test.yml
Group: DMS_TEST_GROUP
NameSpace:test
配置格式:YAML
內(nèi)容:
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
serverAddr: 192.168.10.164:8848
namespace: seata
group: SEATA_GROUP
username: nacos
password: nacos
dataId: "seataServer.properties"
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
serverAddr: 192.168.10.164:8848
namespace: seata
group: SEATA_GROUP
username: nacos
password: nacos
2)創(chuàng)建seata客戶端的多數(shù)據(jù)源(druid)配置文件
Data ID:druid_dynamic_test.yml
Group: DMS_TEST_GROUP
NameSpace:test
配置格式:YAML
內(nèi)容:
spring:
datasource:
default-transaction-isolation: 2
druid:
aop-patterns: com.ruoyi.*,com.zlt.*
stat-view-servlet: #登陸賬號密碼
login-password: root
login-username: root
reset-enable: true
enabled: true
allow: 192.168.2.*,127.0.0.1,192.168.10.*
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: /*.js,/*.gif,/*.jpg,/*.bmp,/*.png,/*.css,/*.ico,/druid/*
filter:
wall:
enabled: false
config:
multi-statement-allow: true
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 10000
merge-sql: true
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: oracle.jdbc.driver.OracleDriver
# url: jdbc:oracle:thin:@192.168.2.101:1521/zlt
# username: rycloud
# password: rycloud
dynamic:
druid:
# 下面為連接池的補充設置埋市,應用到上面所有數(shù)據(jù)源中
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
# 配置監(jiān)控統(tǒng)計攔截的filters食听,去掉后監(jiān)控界面sql無法統(tǒng)計,'wall'用于防火墻,防止sql注入
#filters: stat,slf4j
#connectionProperties: druid.stat.mergeSql\\=true;druid.stat.slowSqlMillis\\=5000
datasource:
# 主庫數(shù)據(jù)源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@192.168.2.101:1521/zlt
username: ll_ems
password: ll_ems
# 審批數(shù)據(jù)源
activiti:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@192.168.2.101:1521/zlt
username: ll_ems
password: ll_ems
# 消息數(shù)據(jù)源
message:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@192.168.2.101:1521/zlt
username: rycloud
password: rycloud
seata: true
# 開啟seata代理培己,開啟后默認每個數(shù)據(jù)源都代理碳蛋,如果某個不需要代理可單獨關閉
3)每個應用服務的yml文件胚泌,增補seata開啟信息
eg.
Data ID:dms-mt-biz-test.yml
Group: DMS_TEST_GROUP
NameSpace:test
配置格式:YAML
增補內(nèi)容:
# seata配置
seata:
# 默認關閉省咨,如需啟用spring.datasource.dynami.seata需要同時開啟
enabled: true
# Seata 應用編號,默認為 ${spring.application.name}
application-id: ${spring.application.name}
# 關閉自動代理
enableAutoDataSourceProxy: false
當然玷室,上述1零蓉、2、3的yml文件也可以合并為一個穷缤。若有細分的話敌蜂,應在服務的bootstrap.yml啟動文件中將上述共享配置納入
如:
4)每個應用服務都要創(chuàng)建UNDO_LOG表
MYSQL腳本:
CREATE TABLE
undo_log
(
id bigint NOT NULL AUTO_INCREMENT,
branch_id bigint NOT NULL,
xid VARCHAR(100) NOT NULL,
context VARCHAR(128) NOT NULL,
rollback_info LONGBLOB NOT NULL,
log_status INT NOT NULL,
log_created DATETIME NOT NULL,
log_modified DATETIME NOT NULL,
ext VARCHAR(100),
PRIMARY KEY (id),
CONSTRAINT ux_undo_log UNIQUE (xid, branch_id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;
ORACLE腳本:
CREATE TABLE
UNDO_LOG
(
ID NUMBER(19) NOT NULL,
BRANCH_ID NUMBER(19) NOT NULL,
XID VARCHAR2(100) NOT NULL,
CONTEXT VARCHAR2(128) NOT NULL,
ROLLBACK_INFO BLOB NOT NULL,
LOG_STATUS NUMBER(10) NOT NULL,
LOG_CREATED TIMESTAMP(0) NOT NULL,
LOG_MODIFIED TIMESTAMP(0) NOT NULL,
PRIMARY KEY (ID),
CONSTRAINT UX_UNDO_LOG UNIQUE (XID, BRANCH_ID)
);
COMMENT ON TABLE UNDO_LOG
IS
'AT transaction mode undo table';
5)啟動應用服務
若在Seata服務端可以看到應用服務(客戶端)的注冊信息,則表示客戶端的配置是正常的津肛;
三章喉、使用Seata分布式事務
1、在@Service服務里使用GlobalTransactional全局事務身坐;
注意:一定要在Service服務里秸脱,不能在Controller,否則全局事務不會生效
參考代碼:
/**
* 提交審批
* @param billVo 單據(jù)VO
* @param variables 提交審批流變量
* @param callBackFunc 提交后回調(diào)
*/
@DS("master")
@Transactional(rollbackFor = Exception.class)
@GlobalTransactional(rollbackFor = Exception.class)
public void submitApply(T billVo, Map<String, Object> variables,
Function<List, Object> callBackFunc) throws TransactionException {
//1.提交審批
String applyUserId = variables.get(ActivitiConstants.CURRENT_LOGIN_USER_NAME) == null ? SecurityUtils.getUsername()
: String.valueOf(variables.get(ActivitiConstants.CURRENT_LOGIN_USER_NAME));
ActProcessInstance processInstance = actProcessService.submitApply(applyUserId,String.valueOf(billVo.getId()), variables);
//2.提交后回調(diào)
if (callBackFunc != null){
if (processInstance == null || StringUtils.isBlank(processInstance.getProcessInstanceId())){
//Fegin調(diào)用使用了Fallback降級或拋出的異常被全局處理
//這種情況下屬于seata服務發(fā)現(xiàn)不了下游服務拋出的異常部蛇,導致事務不會觸發(fā)回滾,需手動回滾
GlobalTransactionContext.reload(RootContext.getXID()).rollback();
throw new CustomException("提交審批失斕健:審批流實例未能正常返回!");
}
List<Object> paramsList = new ArrayList<>();
paramsList.add(billVo);
paramsList.add(processInstance.getProcessInstanceId());
callBackFunc.apply(paramsList);
}
}
import com.baomidou.dynamic.datasource.annotation.DS;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransactionContext;
2涯鲁、分支事務Transactional巷查,使用REQUIRES_NEW
/**
* 事務傳播特性設置為 REQUIRES_NEW 開啟新的事務 重要S行颉!5呵搿旭寿!一定要使用REQUIRES_NEW
*/
@DS("activiti")
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void complete(String taskId, String instanceId, Map<String, Object> variables) {
//審批動作
}
3、調(diào)試注意事項
1)盡量不在要服務間設置斷點崇败,會引起超時许师;
2)啟動全局事務后,在控制臺就可以看到全局事務信息僚匆;
3)分支事務執(zhí)行后微渠,每個服務的業(yè)務操作在undo_log會自動記錄,但很快會回滾消失咧擂,所以有時不要以為沒有生成undo_log日志逞盆;
四、注意事項
1松申、若是Oracle版本云芦,建議引入ojdbc6驅(qū)動,減少出現(xiàn)莫名的問題
<!-- Seata分布式事務需要的版本 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle6.version}</version>
</dependency>
<oracle6.version>12.1.0.1-atlassian-hosted</oracle6.version>
2贸桶、在事務過程中舅逸,不要自行攔截異常,否則seata會捕獲不到皇筛;
3琉历、若Fegin調(diào)用使用了Fallback降級或拋出的異常被全局處理;
這種情況下屬于seata服務發(fā)現(xiàn)不了下游服務拋出的異常水醋,導致事務不會觸發(fā)回滾
解決辦法:
通過 GlobalTransactionContext.reload(RootContext.getXID()).rollback() 進行手動回滾旗笔;
if (processInstance == null || StringUtils.isBlank(processInstance.getProcessInstanceId())){
//Fegin調(diào)用使用了Fallback降級或拋出的異常被全局處理
//這種情況下屬于seata服務發(fā)現(xiàn)不了下游服務拋出的異常,導致事務不會觸發(fā)回滾,需手動回滾
GlobalTransactionContext.reload(RootContext.getXID()).rollback();
throw new CustomException("提交審批失斨糇佟:審批流實例未能正常返回蝇恶!");
}
4、所有參與分布式事務的庫表惶桐,必須要有主鍵撮弧,且必須是唯一的主鍵,不能聯(lián)合主鍵姚糊,否則會出現(xiàn): get table meta error:Failed to fetch schema of 表名贿衍。
5、Seata不支持的庫表字段類型:NVarchar2
6叛拷、日志打印XID發(fā)現(xiàn)沒有值或不一樣
若是使用feign調(diào)用舌厨,則需要引入seata-spring-boot-starter
7、出現(xiàn):Response[ TransactionException[Could not register branch into global session xid = xxx.xxx.xx.xx:xx
原因:Seata的AT模型調(diào)用其他服務時是異步的忿薇。seata的全局事務超時時間設置太短了裙椭,導致注冊分支事務的時候躏哩,全局事務都已經(jīng)進入第二階段了。將配置文件中的事務超時等待設置長些即可:如圖(如果60秒不夠用可以在設置大些揉燃,但是對應的代碼中全局事務超時(@GlobalTransactional(timeoutMills = 默認60秒))也要設置大些)
8扫尺、支持日期類型,使用kyro序列化
在seataServer.properties中炊汤,修改logSerialization
client.undo.dataValidation=true
client.undo.logSerialization=kryo
client.undo.onlyCareUpdateColumns=true
在應用服務的pom文件中正驻,引入kyro相關依賴
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>${kryo.version}</version>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>${kryo-serializers.version}</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>${kryo-software.version}</version>
</dependency>
<kryo.version>2.24.0</kryo.version>
<kryo-serializers.version>0.45</kryo-serializers.version>
<kryo-software.version>4.0.2</kryo-software.version>