分布式事务管理框架Seata应用完全解析
Seata(Simple Extensible Autonomous Transaction Architecture) 是阿里巴巴开源的分布式事务中间件,以高效并且对业务零侵入的方式,解决微服务场景下面临的分布式事务问题。
Seata(Simple Extensible Autonomous Transaction Architecture) 是阿里巴巴开源的分布式事务中间件,以高效并且对业务零侵入的方式,解决微服务场景下面临的分布式事务问题。
一、分布式事务管理中的 CAP定理
1、CAP 定理(也称为 Brewer 定理),指的是在分布式计算环境下,有3个核心的要求
(1)一致性(Consistency),所有实例节点同一时间看到是相同的数据
(2)可用性(Availability),不管是否成功,确保每一个请求都能接收到响应
(3)分区容错性(Partition Tolerance),系统任意分区后,在网络故障时,仍能操作
2、CAP理论告诉我们,分布式系统不可能同时满足以三种要求
最多只能同时满足其中的两项,大多数分布式业务中P是必须的, 因此往往选择就在CP或者AP。
(1)CA: 放弃分区容错性。非分布式架构,比如关系数据库,因为没有分区,但是在分布式系统下,CA组合就不建议了。
(2)AP: 放弃强一致性。追求最终一致性,类似的场景比如转账,可以接受两小时后到账,Eureka的注册也是类似的做法。
(3)CP: 放弃可用性。zookeeper在leader宕机后,选举期间是不提供服务的。类似的场景比如支付完成之后出订单,必须一进一出都完成才行。
说明:在分布式系统中AP运用的最多,因为它放弃的是强一致性,追求的是最终一致性,性价比最高。
二、分布式事务管理AT模式
分布式事务Seata有三种模式,这里重点介绍一下AT模式。
AT:两阶段提交协议。
TCC:是服务化的两阶段提交协议,业务开发者需要实现这三个服务接口。
第一阶段服务由业务代码编排来调用 Try 接口进行资源预留,所有参与者的 Try 接口都成功了,事务管理器会提交事务,并调用每个参与者的 Confirm 接口真正提交业务操作,否则调用每个参与者的 Cancel 接口回滚事务。
Saga:是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
1.1 AT模式角色分析
官方原文:
Transaction Coordinator(TC): Maintain status of global and branch transactions, drive the global commit or rollback. Transaction Manager(TM): Define the scope of global transaction: begin a global transaction, commit or rollback a global transaction. Resource Manager(RM): Manage resources that branch transactions working on, talk to TC for registering branch transactions and reporting status of branch transactions, and drive the branch transaction commit or rollback.
原文解释:
1、Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
2、Transaction Manager (TM) : 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
3、Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
2.2 AT模式工作流程
1、一阶段提交
注册分支事务,记录undo-log、执行业务sql并提交、报告事务状态;
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
2、二阶段提交
如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成。
3、二阶段回滚
如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。
三、Seata应用
3.1 下载seata-server
官网地址:
https://github.com/apache/incubator-seata/releases/download/v2.0.0/seata-server-2.0.0.zip
解压seata-server-2.0.0.zip文件,将解压后的seata文件夹拷贝到D:\programd下。
3.2 配置seata-sever
1、
配置seata-server数据源与数据库
(1)配置seata-server数据源
配置文件file.conf设置(没有就创建一个): D:\programd\seata\conf\file.conf
(2)创建seata数据库
create database seata
(3)创建3张表
创建表global_table:
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;
创建表branch_table:
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;
创建表lock_table:
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`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;
2、修改seata的注册中心
配置文件为(没有就创建一个)D:\programd\seata\conf\registry.conf
3、修改seata的配置中心
配置文件还是D:\programd\seata\conf\registry.conf
4、通过Nacos配置中心管理seata配置
(1)下载配置项:https://github.com/seata/seata/tree/develop/script/config-center
内容如下:
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.fengmi_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=db
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/sdb?useUnicode=true
store.db.user=root
store.db.password=1234
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
(2)个性相关配置项
service.vgroupMapping.fengmi_tx_group=default
store.mode=db
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/sdb?useUnicode=true
store.db.user=root
store.db.password=1234
(3)创建配置文件application.properties, 目前只支持properties
, 不支持yml
格式。
(4)启动seata-server
切换到seata的bin文件夹命令窗口输入:seata-server.bat -h 127.0.0.1 -p 8098
启动后效果如下:
四、Seata客户端
一个调用链中的所有微服务都是seata的客户端,都必须走下面的步骤。
1、创建undo_log表
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';
2、pom依赖
<!-- seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
<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>1.4.2</version>
</dependency>
3、配置设置
seata:
enabled: true
tx-service-group: fengmi_tx_group
enable-auto-data-source-proxy: true
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
namespace: sit
username: nacos
password: nacos
data-id: fengmi_tx_group-sit.properties
registry: #发现seata-server
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: sit
group: DEFAULT_GROUP
username: nacos
password: nacos
4、在Service与Controller类中的方法上添加注解@Globaltransational
五、Seata全局事务并发隔离
1、读隔离
一个全局事务修改数据,另外一个全局读取数据怎么解决脏读。
在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata(AT模式)的默认全局隔离级别是读未提交(Read Uncommitted)。
如果应用在特定场景下,必需要求全局的读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE语句的代理。
2、写隔离
一阶段本地事务提交前,需要确保先拿到 全局锁 。
拿不到 全局锁 ,不能提交本地事务。
更多精彩内容请关注本站!!!
更多推荐
所有评论(0)