SEATA(Simple Extensible Autonomous Transaction Architecture)

一、SEATA是什么?

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

在继续学习使用SEATA之前,对seata介绍中提到的分布式事务、AT、TCC、SAGA 和 XA 事务模式这些名词有必要介绍一下。

1.什么是分布式事务?


首先说下事务,事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。

事务应该具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。

事务更多指的是单机版、单数据库的概念。分布式事务用于在分布式系统中保证不同节点之间的数据一致性。

漫画: 什么是分布式事务?

2. XA规范


有了分布式事务的场景,就会有解决该问题的方式规范,XA规范就是解决分布式事务的规范。分布式事务的实现方式有很多种,最具有代表性的是由Oracle Tuxedo系统提出的 XA分布式事务协议。XA协议包括两阶段提交(2PC)和三阶段提交(3PC)两种实现。

两阶段提交(2PC)

两阶段提交又称2PC(two-phase commit protocol),2pc是一个非常经典的强一致、中心化的原子提交协议。这里所说的中心化是指协议中有两类节点:一个是中心化协调者节点(coordinator)和N个参与者节点(partcipant)。

准备阶段     事务协调者,向所有事务参与者发送事务内容,询问是否可以提交事务,并等待参与者回复。     事务参与者收到事务内容,开始执行事务操作,讲 undo 和 redo 信息记入事务日志中(但此时并不提交事务)。     如果参与者执行成功,给协调者回复yes,表示可以进行事务提交。如果执行失败,给协调者回复no,表示不可提交。

XA一阶段提交

提交阶段     如果协调者收到了参与者的失败信息或超时信息,直接给所有参与者发送回滚(rollback)信息进行事务回滚,否则,发送提交(commit)信息。     参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源) 。

[图片上传失败...(image-a6b970-1618380177779)]


xa规范2pc异常提交阶段

以下几点是XA-两阶段提交协议中会遇到的一些问题:

  • 性能问题

从流程上我们可以看得出,其最大缺点就在于它的执行过程中间,节点都处于阻塞状态。各个操作数据库的节点此时都占用着数据库资源,只有当所有节点准备完毕,事务协调者才会通知进行全局提交,参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。

  • 协调者单点故障问题

事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,会导致参与者收不到提交或回滚的通知,从而导致参与者节点始终处于事务无法完成的中间状态。

  • 丢失消息导致的数据不一致问题

在第二个阶段,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就会导致节点间数据的不一致问题。

2PC 方案实现起来简单,基于上面提到的两阶段提交协议中会遇到的问题,实际项目中使用的比较少。那么有没有其他的方案来解决呢?

三阶段提交(3PC)

三阶段提交是在二阶段提交上的改进版本,其在两阶段提交的基础上增加了 CanCommit阶段,并加入了超时机制。同时在协调者和参与者中都引入超时机制。三阶段将二阶段的准备阶段拆分为2个阶段,插入了一个preCommit阶段,以此来处理原先二阶段,参与者准备后,参与者发生崩溃或错误,导致参与者无法知晓是否提交或回滚的不确定状态所引起的延时问题。

阶段 1:canCommit

  • 协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所有参与者答复。

  • 参与者收到 canCommit 请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否则反馈 no。

xa规范3pc-canCommit

阶段 2:preCommit

阶段一中,如果所有的参与者都返回Yes的话,那么就会进入PreCommit阶段进行事务预提交。此时分布式事务协调者会向所有的参与者节点发送PreCommit请求,参与者收到后开始执行事务操作,并将Undo和Redo信息记录到事务日志中。参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示我已经准备好提交了,并等待协调者的下一步指令。如果阶段一中有任何一个参与者节点返回的结果是No响应,或者协调者在等待参与者节点反馈的过程中因挂掉而超时(2PC中只有协调者可以超时,参与者没有超时机制)。整个分布式事务就会中断,协调者就会向所有的参与者发送“abort”请求。

xa规范3pc-preCommit

阶段 3:do Commit

该阶段进行真正的事务提交,在阶段二中如果所有的参与者节点都可以进行PreCommit提交,那么协调者就会从“预提交状态” 转变为 “提交状态”。然后向所有的参与者节点发送"doCommit"请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的Ack消息后完成事务。

xa规范3pc-doCommit

相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。这解决了一个什么问题呢?这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。

另外,通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的。

以上就是3PC相对于2PC的一个提高(相对缓解了2PC中的前两个问题),但是3PC依然没有完全解决数据不一致的问题。假如在 DoCommit 过程,参与者A无法接收协调者的通信,那么参与者A会自动提交,但是提交失败了,其他参与者成功了,此时数据就会不一致。

3. AT(Auto Transaction)模式


AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

AT 模式如何做到对业务的无侵入

一阶段

在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

以update语句为例:

update user set name = 'name_1' where name = 'name_0'

首先 Seata 的 JDBC数据源代理通过对业务 SQL 解析,提取 SQL 的元数据,也就是得到 SQL 的类型(UPDATE),表(user),条件(where id= 1)等相关的信息。

提取表元数据:

select  id,name from user where name = 'name_0'
img

将查询到的结果如上图所示保存为“before image”,执行“业务 SQL”更新业务数据SQL。根据前镜像数据主键查询出后镜像数据,查询结果为:

select  id,name from user where id = 1
img

把业务数据在更新前后的数据镜像组织成回滚日志,将业务数据的更新和回滚日志在同一个本地事务中提交,分别插入到业务表和 UNDO_LOG 表中。

img
二阶段提交

二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

img
二阶段回滚

二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

img

总结

AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。但AT模式存在的不足就是 当操作的数据 是共享型数据,会存在脏写的问题,所以如果是 用户独有数据可以使用AT模式。

4.TCC(Try、Confirm、Cancel)模式


TCC方案其实是两阶段提交的一种改进。分成了Try、Confirm、Cancel三个操作。事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。@TwoPhaseBusinessAction是TCC服务参与者必须加的注解,指定服务名称,提交方法commitMethod及回滚方法rollbackMethod,SecondAction同理

  • Try部分完成业务的准备工作

  • confirm部分完成业务的提交

  • cancel部分完成事务的回滚

    TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。

  • 二阶段 commit 行为:调用 自定义 的 commit 逻辑。

  • 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。 所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。简单点概括,SEATA的TCC模式就是手工的AT模式,它允许你自定义两阶段的处理逻辑而不依赖AT模式的undo_log。

  • @LocalTCC 适用于SpringCloud+Feign模式下的TCC

  • @TwoPhaseBusinessAction 注解try方法,其中name为当前tcc方法的bean名称,写方法名便可(记得全局唯一),commitMethod指向提交方法,rollbackMethod指向事务回滚方法。指定好三个方法之后,seata会根据全局事务的成功或失败,去帮我们自动调用提交方法或者回滚方法。

  • @BusinessActionContextParameter 注解可以将参数传递到二阶段(commitMethod/rollbackMethod)的方法。

  • BusinessActionContext 便是指TCC事务上下文


TCC实践,总结以下注意事项:

➢ 业务模型分2阶段设计 ➢ 并发控制 ➢ 允许空回滚 ➢ 防悬挂控制 ➢ 幂等控制

用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功。相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。

5.SAGA模式


SAGA简介

Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

Saga模式示意图

如图:T1-T3都是正向的业务流程,都对应着一个冲正逆向操作C1-C3。

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 正向服务与补偿服务也需要业务开发者实现。因此是业务入侵的。

Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

Saga 模式使用场景

Saga 模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。

事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,可以使用 Saga 模式。

Saga模式的优势与缺点

优势

  • 一阶段提交本地数据库事务,无锁,高性能;

  • 参与者可以采用事务驱动异步执行,高吞吐

  • 补偿服务即正向服务的“反向”,易于理解,易于实现;

缺点

Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。

注意

与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:

  • 空补偿:逆向操作早于正向操作时;

  • 防悬挂控制:空补偿后要拒绝正向操作

  • 幂等

总结 AT、TCC、Saga、XA 模式分析

四种分布式事务模式,分别在不同的时间被提出,每种模式都有它的适用场景:

  • AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,几乎0学习成本。

  • TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。

  • Saga 模式是长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。

  • XA模式是分布式强一致性的解决方案,但性能低而使用较少。

二.SEATA术语

TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

三.Seata整合Nacos

1.环境准备

seata软件下载地址 http://seata.io/zh-cn/blog/download.html

1.1 file.conf

本地windows下载zip解压后进入conf目录,修改file.conf:

file.conf

1.2 register.conf

修改register.conf,这里是整合nacos,只保留了nacos的配置:

register.conf

1.3 nacos-config.sh

接下来将配置导入到nacos中需要在conf文件夹内,新建一个nacos-config.sh文件,这个文件1.4.0版本是没有的。github下载地址:

nacos-config.sh

#!/usr/bin/env bash
# Copyright 1999-2019 Seata.io Group.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at、
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

while getopts ":h:p:g:t:u:w:" opt
do
  case $opt in
  h)
    host=$OPTARG
    ;;
  p)
    port=$OPTARG
    ;;
  g)
    group=$OPTARG
    ;;
  t)
    tenant=$OPTARG
    ;;
  u)
    username=$OPTARG
    ;;
  w)
    password=$OPTARG
    ;;
  ?)
    echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] "
    exit 1
    ;;
  esac
done

urlencode() {
  for ((i=0; i < ${#1}; i++))
  do
    char="${1:$i:1}"
    case $char in
    [a-zA-Z0-9.~_-]) printf $char ;;
    *) printf '%%%02X' "'$char" ;;
    esac
  done
}

if [[ -z ${host} ]]; then
    host=localhost
fi
if [[ -z ${port} ]]; then
    port=8848
fi
if [[ -z ${group} ]]; then
    group="SEATA_GROUP"
fi
if [[ -z ${tenant} ]]; then
    tenant=""
fi
if [[ -z ${username} ]]; then
    username=""
fi
if [[ -z ${password} ]]; then
    password=""
fi

nacosAddr=$host:$port
contentType="content-type:application/json;charset=UTF-8"

echo "set nacosAddr=$nacosAddr"
echo "set group=$group"

failCount=0
tempLog=$(mktemp -u)
function addConfig() {
  curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null
  if [[ -z $(cat "${tempLog}") ]]; then
    echo " Please check the cluster status. "
    exit 1
  fi
  if [[ $(cat "${tempLog}") =~ "true" ]]; then
    echo "Set $1=$2 successfully "
  else
    echo "Set $1=$2 failure "
    (( failCount++ ))
  fi
}

count=0
for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do
  (( count++ ))
    key=${line%%=*}
    value=${line#*=}
    addConfig "${key}" "${value}"
done

echo "========================================================================="
echo " Complete initialization parameters,  total-count:$count ,  failure-count:$failCount "
echo "========================================================================="

if [[ ${failCount} -eq 0 ]]; then
    echo " Init nacos config finished, please start seata-server. "
else
    echo " init nacos config fail. "
fi

1.4 config.txt

在conf同级目录下,需要新建config.txt文件,1.4.0版本也没有,下载地址为:

config.txt

修改config.txt数据库连接信息为自己的数据库。

transport.type=TCP
 transport.server=NIO
 transport.heartbeat=true
 transport.enableClientBatchSendRequest=false
 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
 service.vgroupMapping.my_test_tx_group=default
 service.default.grouplist=127.0.0.1:8091
 service.enableDegrade=false
 service.disableGlobalTransaction=false
 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=false
 client.rm.tableMetaCheckerInterval=60000
 client.rm.sqlParserType=druid
 client.rm.reportSuccessEnable=false
 client.rm.sagaBranchRegisterEnable=false
 client.tm.commitRetryCount=5
 client.tm.rollbackRetryCount=5
 client.tm.defaultGlobalTransactionTimeout=60000
 client.tm.degradeCheck=false
 client.tm.degradeCheckAllowTimes=10
 client.tm.degradeCheckPeriod=2000
 store.mode=file
 store.publicKey=
 store.file.dir=file_store/data
 store.file.maxBranchSessionSize=16384
 store.file.maxGlobalSessionSize=512
 store.file.fileWriteBufferCacheSize=16384
 store.file.flushDiskMode=async
 store.file.sessionReloadReadSize=100
 store.db.datasource=druid
 store.db.dbType=mysql
 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=root
 store.db.password=
 store.db.minConn=5
 store.db.maxConn=30
 store.db.globalTable=global_table
 store.db.branchTable=branch_table
 store.db.queryLimit=100
 store.db.lockTable=lock_table
 store.db.maxWait=5000
 store.redis.mode=single
 store.redis.single.host=127.0.0.1
 store.redis.single.port=6379
 store.redis.maxConn=10
 store.redis.minConn=1
 store.redis.maxTotal=100
 store.redis.database=0
 store.redis.password=
 store.redis.queryLimit=100
 server.recovery.committingRetryPeriod=1000
 server.recovery.asynCommittingRetryPeriod=1000
 server.recovery.rollbackingRetryPeriod=1000
 server.recovery.timeoutRetryPeriod=1000
 server.maxCommitRetryTimeout=-1
 server.maxRollbackRetryTimeout=-1
 server.rollbackRetryTimeoutUnlockEnable=false
 client.undo.dataValidation=true
 client.undo.logSerialization=jackson
 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
 log.exceptionRate=100
 transport.serialization=seata
 transport.compressor=none
 metrics.enabled=false
 metrics.registryType=compact
 metrics.exporterList=prometheus
 metrics.exporterPrometheusPort=9898

上面我们下载的这两个文件,其中config.txt是seata各种详细的配置,执行 nacos-config.sh 即可将这些配置导入到nacos,这样就不需要将file.conf和registry.conf放到我们的项目中了,需要什么配置就直接从nacos中读取。

1.5 seata全属性

公共部分:

key desc remark
transport.serialization client和server通信编解码方式 seata(ByteBuf)、protobuf、kryo、hession、fst,默认seata
transport.compressor client和server通信数据压缩方式 none、gzip,默认none
transport.heartbeat client和server通信心跳检测开关 默认true开启
registry.type 注册中心类型 默认file,支持file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom
config.type 配置中心类型 默认file,支持file、nacos 、apollo、zk、consul、etcd3、custom

server端

key desc remark
server.undo.logSaveDays undo保留天数 默认7天,log_status=1(附录3)和未正常清理的undo
server.undo.logDeletePeriod undo清理线程间隔时间 默认86400000,单位毫秒
server.maxCommitRetryTimeout 二阶段提交重试超时时长 单位ms,s,m,h,d,对应毫秒,秒,分,小时,天,默认毫秒。默认值-1表示无限重试。公式: timeout>=now-globalTransactionBeginTime,true表示超时则不再重试
server.maxRollbackRetryTimeout 二阶段回滚重试超时时长 同commit
server.recovery.committingRetryPeriod 二阶段提交未完成状态全局事务重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.asynCommittingRetryPeriod 二阶段异步提交状态重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.rollbackingRetryPeriod 二阶段回滚状态重试回滚线程间隔时间 默认1000,单位毫秒
server.recovery.timeoutRetryPeriod 超时状态检测重试线程间隔时间 默认1000,单位毫秒,检测出超时将全局事务置入回滚会话管理器
store.mode 事务会话信息存储方式 file本地文件(不支持HA),db数据库 redis(支持HA)
store.file.dir file模式文件存储文件夹名 默认sessionStore
store.db.datasource db模式数据源类型 dbcp、druid、hikari;无默认值,store.mode=db时必须指定。
store.db.dbType db模式数据库类型 mysql、oracle、db2、sqlserver、sybaee、h2、sqlite、access、postgresql、oceanbase;无默认值,store.mode=db时必须指定。
store.db.driverClassName db模式数据库驱动 store.mode=db时必须指定
store.db.url db模式数据库url store.mode=db时必须指定,在使用mysql作为数据源时,建议在连接参数中加上rewriteBatchedStatements=true(详细原因请阅读附录7)
store.db.user db模式数据库账户 store.mode=db时必须指定
store.db.password db模式数据库账户密码 store.mode=db时必须指定
store.db.minConn db模式数据库初始连接数 默认1
store.db.maxConn db模式数据库最大连接数 默认20
store.db.maxWait db模式获取连接时最大等待时间 默认5000,单位毫秒
store.db.globalTable db模式全局事务表名 默认global_table
store.db.branchTable db模式分支事务表名 默认branch_table
store.db.lockTable db模式全局锁表名 默认lock_table
store.db.queryLimit db模式查询全局事务一次的最大条数 默认100
store.redis.host redis模式ip 默认127.0.0.1
store.redis.port redis模式端口 默认6379
store.redis.maxConn redis模式最大连接数 默认10
store.redis.minConn redis模式最小连接数 默认1
store.redis.database redis模式默认库 默认0
store.redis.password redis模式密码(无可不填) 默认null
store.redis.queryLimit redis模式一次查询最大条数 默认100
metrics.enabled 是否启用Metrics 默认false关闭,在False状态下,所有与Metrics相关的组件将不会被初始化,使得性能损耗最低
metrics.registryType 指标注册器类型 Metrics使用的指标注册器类型,默认为内置的compact(简易)实现,这个实现中的Meter仅使用有限内存计数,性能高足够满足大多数场景;目前只能设置一个指标注册器实现
metrics.exporterList 指标结果Measurement数据输出器列表 默认prometheus,多个输出器使用英文逗号分割,例如"prometheus,jmx",目前仅实现了对接prometheus的输出器
metrics.exporterPrometheusPort prometheus输出器Client端口号 默认9898

client端

key desc remark
seata.enabled 是否开启spring-boot自动装配 true、false,(SSBS)专有配置,默认true(附录4)
seata.enableAutoDataSourceProxy=true 是否开启数据源自动代理 true、false,seata-spring-boot-starter(SSBS)专有配置,SSBS默认会开启数据源自动代理,可通过该配置项关闭.
seata.useJdkProxy=false 是否使用JDK代理作为数据源自动代理的实现方式 true、false,(SSBS)专有配置,默认false,采用CGLIB作为数据源自动代理的实现方式
transport.enableClientBatchSendRequest 客户端事务消息请求是否批量合并发送 默认true,false单条发送
client.log.exceptionRate 日志异常输出概率 默认100,目前用于undo回滚失败时异常堆栈输出,百分之一的概率输出,回滚失败基本是脏数据,无需输出堆栈占用硬盘空间
service.vgroupMapping.my_test_tx_group 事务群组(附录1) my_test_tx_group为分组,配置项值为TC集群名
service.default.grouplist TC服务列表(附录2) 仅注册中心为file时使用
service.disableGlobalTransaction 全局事务开关 默认false。false为开启,true为关闭
client.tm.degradeCheck 降级开关 默认false。业务侧根据连续错误数自动降级不走seata事务(详细介绍请阅读附录6)
client.tm.degradeCheckAllowTimes 升降级达标阈值 默认10
client.tm.degradeCheckPeriod 服务自检周期 默认2000,单位ms.每2秒进行一次服务自检,来决定
client.rm.reportSuccessEnable 是否上报一阶段成功 true、false,从1.1.0版本开始,默认false.true用于保持分支事务生命周期记录完整,false可提高不少性能
client.rm.asynCommitBufferLimit 异步提交缓存队列长度 默认10000。 二阶段提交成功,RM异步清理undo队列
client.rm.lock.retryInterval 校验或占用全局锁重试间隔 默认10,单位毫秒
client.rm.lock.retryTimes 校验或占用全局锁重试次数 默认30
client.rm.lock.retryPolicyBranchRollbackOnConflict 分支事务与其它全局回滚事务冲突时锁策略 默认true,优先释放本地锁让回滚成功
client.rm.reportRetryCount 一阶段结果上报TC重试次数 默认5次
client.rm.tableMetaCheckEnable 自动刷新缓存中的表结构 默认false
client.tm.commitRetryCount 一阶段全局提交结果上报TC重试次数 默认1次,建议大于1
client.tm.rollbackRetryCount 一阶段全局回滚结果上报TC重试次数 默认1次,建议大于1
client.undo.dataValidation 二阶段回滚镜像校验 默认true开启,false关闭
client.undo.logSerialization undo序列化方式 默认jackson
client.undo.logTable 自定义undo表名 默认undo_log

在seata的conf目录下,通过git-bash执行命令:

sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t seata-namespace -u nacos -w nacos
 
 -h  host:nacos主机名
 -p 端口号
 -g group组名称,配置的分组
 -t 命名空间,我这里添加的时候指定为seata-namespace,不设置默认nacos会生成uuid.
 -u 用户名,需要nacos开启权限
 -w 密码

执行完命令成功标志如下图所示:

成功导入

在nacos页面可以看到seata的各种配置。

seata配置

1.6 数据库配置

新建的数据库还有我们的业务的数据库,也需要进行相应的表的建立。这里需要两个sql文件,在1.4.0版本中也是没有的,可从0.9.0版本中获取。其中db_store.sql在seata数据库中执行,db_undo_log.sql在业务数据库(这里我新建一个test_seata库作为业务库来使用)中执行。

img
db_store.sql
db_undo_log.sql

1.7 logs文件夹

这个时候直接启动会报没有log文件的异常,在conf同级目录下新建logs文件夹,在logs文件夹中,新建一个seat_gc.log文件。

1.8 启动seata

进入bin目录,直接双击seata-server.bat启动即可,启动成功后可在nacos服务列表查看到seata-server服务。

img

2.项目配置

版本信息:

 springcloud alibaba version:2.1.3.RELEASE
 springcloud version:Greenwich.SR2
 springboot version:2.1.6.RELEASE
 spring-cloud-starter-alibaba-seata version:2.1.3.RELEASE

启动nacos,seata服务。

2.1 子工程引入seata依赖

我们需要在项目中引入seata的jar包,有以下选择(任选其一):

  • 依赖seata-all 手动配置较多

  • 依赖seata-spring-boot-starter,支持yml配置

  • 依赖spring-cloud-starter-alibaba-seata,内部集成了seata,并实现了xid传递

注意:client 版本与 server端版本一致

上面三种方式,需要做不同的事情,尤其xid的传递是比较麻烦的,还好spring-cloud-starter-alibaba-seata已经帮我们实现,具体看看下图:

img

  com.alibaba.cloud
  spring-cloud-starter-alibaba-seata
  
  
  
  io.seata
  seata-spring-boot-starter
  
  
  
  
  
  io.seata
  seata-spring-boot-starter
  1.4.0
  

2.2 数据库准备

2个微服务模拟分布式事务AT模式,提交和回滚。需要新建两个库。两个库都需要新建undo_log表.

  • (seata-storage)仓储服务:对给定的商品扣除仓储数量。

  • (seata-order)订单服务:根据采购需求创建订单。

 CREATE TABLE `undo_log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime(0) NOT NULL,
  `log_modified` datetime(0) NOT NULL,
  `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

(seata-order)订单表:

 CREATE TABLE `order_tbl`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `count` int(11) NULL DEFAULT 0,
  `money` int(11) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

(seata-storage)仓储表:

CREATE TABLE `storage_tbl`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `count` int(11) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `commodity_code`(`commodity_code`) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
 
 -- ----------------------------
 -- Records of storage_tbl
 -- ----------------------------
 INSERT INTO `storage_tbl` VALUES (1, 'product-1', 998);
 INSERT INTO `storage_tbl` VALUES (2, 'product-2', 0);

2.3 YML配置

两个服务都需要配置以下数据库链接,以及seata相关内容

spring:
  datasource:
  type: com.alibaba.druid.pool.DruidDataSource
  driverClassName: com.mysql.cj.jdbc.Driver
  druid:
  master:
  url: jdbc:mysql://127.0.01:3306/order_seata?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
  username: root
  password: 
  slave:
  enabled: false
  url:
  username:
  password:
  initialSize: 5
  minIdle: 10
  maxActive: 20
  maxWait: 60000
  timeBetweenEvictionRunsMillis: 60000
  minEvictableIdleTimeMillis: 300000
  maxEvictableIdleTimeMillis: 900000
  validationQuery: SELECT 1 FROM DUAL
  testWhileIdle: true
  testOnBorrow: false
  testOnReturn: false
  webStatFilter:
  enabled: true
  statViewServlet:
  enabled: true
  allow:
  url-pattern: /druid/*
  login-username:
  login-password:
  filter:
  stat:
  enabled: true
  log-slow-sql: true
  slow-sql-millis: 1000
  merge-sql: true
  wall:
  config:
  multi-statement-allow: true
  redis:
  host: 251.12.39.34
  port: 6111
  password:
  timeout: 10s
 seata:
  enabled: true
  application-id: seata-server
  tx-service-group: my_test_tx_group # 事务群组(可以每个应用独立取名, 也可以使用相同的名字, 注意跟配置文件保持一致)
  enable-auto-data-source-proxy: true #启动自动开启数据源代理
  client:
  rm:
  report-retry-count: 5 # 一阶段结果上报TC充实次数(默认5)
  async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000)
  table-meta-check-enable: false # 自动刷新缓存中的表结构
  report-success-enable: true
  lock:
  retry-interval: 10 # 校验或占用全局锁重试间隔(默认10ms)
  retry-times: 30 # 校验或占用全局锁重试次数(默认30)
  retry-policy-branch-rollback-on-conflict: true
  tm:
  commit-retry-count: 3 # 一阶段全局提交上报 TC 重试次数(默认 1 次, 建议大于 1)
  rollback-retry-count: 3 # 一阶段全局回滚上报 TC 重试次数(默认 1 次, 建议大于 1)
  undo:
  data-validation: true # 二阶段回滚镜像校验(默认 true 开启)
  log-serialization: jackson # undo 序列化方式(默认 jackson)
  log-table: undo_log # 自定义undo表名(默认undo_log)
  log:
  exception-rate: 100 # 日志异常输出概率(默认 100)
  support:
  spring:
  datasource-autoproxy: true
  service:
  enable-degrade: false # 降级开关
  disable-global-transaction: false # 禁用全局事物(默认 false)
  vgroup-mapping:
  my_test_tx_group: default
  registry:
  type: nacos
  nacos:
  application: seata-server
  server-addr: 127.0.0.1:8848
  # 这里的名字就是 registry.conf 中 nacos 的 group 名字
  group: SEATA_GROUP
  username: nacos
  password: nacos
  namespace: seata-namespace
 
 feign:
  client:
  config:
  default:
  # 表示 feign 的请求处理超时时间
  read-timeout: 10000
  # 表示 feign 的连接建立超时时间
  connect-timeout: 10000

只需要使用一个 @GlobalTransactional 注解在业务方法上即可实现分布式事务:

@Override
  @GlobalTransactional(rollbackFor = Exception.class)
  public void placeOrder(String userId, String commodityCode, Integer count) {
 

  BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5));
  Order order = new Order()
  .setId(1)
  .setUserId(userId)
  .setCommodityCode(commodityCode)
  .setCount(count)
  .setMoney(orderMoney);
  //下单:创建订单、减库存,涉及到两个服务
  orderMapper.insertOrder(order);
  storageFeignClient.deduct(commodityCode, count);
 
  }

到这里简单的分布式事务demo即完成了。Seata如此之多的配置和让人摸不到头脑的专业术语到底是怎么使用的才是接下来需要学习的重点。

四. Seata源码学习

img

你可能感兴趣的:(SEATA(Simple Extensible Autonomous Transaction Architecture))