当前位置:首页 > 图灵资讯 > 技术篇> Seata笔记

Seata笔记

发布时间:2023-05-30 09:39:29

产生背景的原因

当架构从单体演变为多服务时,整个系统的可靠性变得难以控制。在单个服务中,从请求到响应结果,请求的整个周期都在服务器上,当地事务可以确保一组数据操作的一致性。

在微服务中,从请求到响应,多个服务器和数据库可能会跨越,当地事务不能保证事务的一致性。

事故不安全案件

在微服务中,从请求到响应,可以跨越多个服务器和数据库。如下图所示,假设有一个金融系统,并将其分为多个微服务。每个微服务都有自己的数据库。我们现在需要使用以下微服务来启动贷款操作:

Seata笔记_seata

 

本贷款操作可抽象概括为以下步骤:(理论简化版)

  1. 用户发起贷款,调用贷款服务的贷款界面;
  2. 贷款同时,在信贷服务中 减少信用额度;
  3. 同时,在资金服务中借款 增加账户余额;
  4. 同时,在日志服务中借款 增加流水记录;

每一项服务都是单独部署的,理想情况下,上述操作都能顺利实施。

若某项服务在执行过程中出现异常:

Seata笔记_seata_02

假设一个常见的场景,资金服务无法正常处理要求,那么这个链接就变成了以下几个方面:

  1. 用户发起贷款,调用贷款服务的贷款界面;
  2. 贷款同时,在信贷服务中 减少信用额度;
  3. 同时,在资金服务中借款 增加账户余额;
  4. 同时,在日志服务中借款 增加流水记录;

本地Transaction在多项服务中已经无法应对这种情况,现在系列操作导致了上述情况,用户信用额度减少,流水也被记录下来,但用户没有收到钱;

Seata简介

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

https://seata.io/zh-cn/docs/overview/what-is-seata.html

目前使用的流行情况如下:AT > TCC > Saga

处理流程

Seata笔记_seata_03

  • Transaction ID XID:全局唯一的事务ID
  • 3组件概念
  • Transaction Coordinator(TC) 事务协调员:维护全局和分支事务状态,推动全局事务提交或回滚。
  • Transaction Manager(TM) 事务管理器:定义全局事务范围:开始全局事务,提交或回滚全局事务。
  • Resource Manager(RM) 资源管理器:管理分支事务处理的资源,与TC交谈,注册分支事务,报告分支事务状态,推动分支事务提交或回滚。
相应的二阶段提交
  • TM开始分布式事务(TM向TC注册全局事务记录);
  • RM向TC报告资源准备状态;
  • TM结束分布式事务(TM通知TC提交/回滚分布式事务);
  • TC汇总事务信息,决定分布式事务是提交还是回滚;
  • TC通知所有RM提交/回滚资源,事务二阶段结束。
AT模式有四种模式

自动化分支事务。无入侵分布式事务解决方案。在AT的触摸下,用户只需关注自己的“业务SQL”,用户的“业务SQL”是第一阶段,Seata 提交和回滚操作将自动生成事务的第二阶段。

Seata笔记_seata_04

整体机制

协议在两个阶段提交的演变:

  • 第一阶段:在同一本地事务中提交业务数据和回滚日志记录,释放本地锁和连接资源,Seata服务端只保留一个全局锁。
  • 二阶段:
  • 提交异步化(异步删除undo log),完成非常快。
  • 回滚通过一阶段的回滚日志进行反向补偿。

与2PC最大的区别在于事务已在第一阶段提交,然后通过反向补偿进行回滚。

一阶段

DML操作时,将回滚日志生成并插入UNDO_LOG表。单机模式、全局事务会话信息内存阅读和写作,并持续本地文件 root.data,性能较高。

  • db 模式:适合集群通过模式,全局事务会话信息通过 db 共享,相对性能差。

我们采用db模式的方法+nacos+seata+ 服务集成;

windows安装在这里;

下载地址

https://seata.io/zh-cn/blog/download.html

创建seata-server解压文件

Seata笔记_seata_07

Nacos创建命名空间

Seata笔记_seata_08

修改配置文件

这里我们使用nacos集成seata-server作为配置中心,直接删除file.conf文件;

Seata笔记_seata_09

修改redistry.conf

nacos注册配置中心的设置如下:

registry {  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa  # 使用注册中心的类型是nacos  type = "nacos"  # nacos相关配置  nacos {    application = "seata-server"    serverAddr = "127.0.0.1:8848"    group = "SEATA_GROUP"    namespace = “0b26125a-4557-4ec5-8afb-853144e126f”    cluster = "default"    username = "nacos"    password = "nacos"  }  eureka {    serviceUrl = "http://localhost:8761/eureka"    application = "default"    weight = "1"  }  redis {    serverAddr = "localhost:6379"    db = 0    password = ""    cluster = "default"    timeout = 0  }  zk {    cluster = "default"    serverAddr = "127.0.0.1:2181"    sessionTimeout = 6000    connectTimeout = 2000    username = ""    password = ""  }  consul {    cluster = "default"    serverAddr = "127.0.0.1:8500"    aclToken = ""  }  etcd3 {    cluster = "default"    serverAddr = "http://localhost:2379"  }  sofa {    serverAddr = "127.0.0.1:9603"    application = "default"    region = "DEFAULT_ZONE"    datacenter = "DefaultDataCenter"    cluster = "default"    group = "SEATA_GROUP"    addressWaitTime = "3000"  }  file {    name = "file.conf"  }}config {  # file、nacos 、apollo、zk、consul、etcd3  # nacoso设置配置中心的类型  type = "nacos"    # nacos相关配置  nacos {    serverAddr = "127.0.0.1:8848"    namespace = “0b26125a-4557-4ec5-8afb-853144e126f”    group = "SEATA_GROUP"    username = "nacos"    password = "nacos"  }  consul {    serverAddr = "127.0.0.1:8500"    aclToken = ""  }  apollo {    appId = "seata-server"    ## apolloConfigService will cover apolloMeta    apolloMeta = "http://192.168.1.204:8801"    apolloConfigService = "http://192.168.1.204:8080"    namespace = "application"    apolloAccesskeySecret = ""    cluster = "seata"  }  zk {    serverAddr = "127.0.0.1:2181"    sessionTimeout = 6000    connectTimeout = 2000    username = ""    password = ""    nodePath = "/seata/seata.properties"  }  etcd3 {    serverAddr = "http://localhost:2379"  }  file {    name = "file.conf"  }}
将配置文件导入nacossnacos

没有配置文件的新版本seata需要手动下载

seata配置文件

  1. config.txt下载完成后,放在bin同级目录下;
  2. nacos将nacos下的nacos-config.sh放在/conf目录下;
  3. 修改config.txt配置文件信息;
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html#Transport configuration, for client and servertransport.type=TCPtransport.server=NIOtransport.heartbeat=truetransport.enableTmClientBatchSendRequest=falsetransport.enableRmClientBatchSendRequest=truetransport.enableTcServerBatchSendResponse=falsetransport.rpcRmRequestTimeout=30000transport.rpcTmRequestTimeout=30000transport.rpcTcRequestTimeout=30000transport.threadFactory.bossThreadPrefix=NettyBosstransport.threadFactory.workerThreadPrefix=NettyServerNIOWorkertransport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandlertransport.threadFactory.shareBossWorker=falsetransport.threadFactory.clientSelectorThreadPrefix=NettyClientSelectortransport.threadFactory.clientSelectorThreadSize=porttransport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThreadtransport.threadFactory.bossThreadSize=porttransport.threadFactory.workerThreadSize=defaulttransport.shutdown.wait=3transport.serialization=seatatransport.compressor=none#Transaction routing rules configuration, only for the clientservice.vgroupMapping.default_tx_group=default#If you use a registry, you can ignore itservice.default.grouplist=127.0.0.1:8091service.enableDegrade=falseservice.disableGlobalTransaction=false#Transaction rule configuration, only for the clientclient.rm.asyncCommitBufferLimit=10000client.rm.lock.retryInterval=10client.rm.lock.retryTimes=30client.rm.lock.retryPolicyBranchRollbackOnConflict=trueclient.rm.reportRetryCount=5client.rm.tableMetaCheckEnable=trueclient.rm.tableMetaCheckerInterval=60000client.rm.sqlParserType=druidclient.rm.reportSuccessEnable=falseclient.rm.sagaBranchRegisterEnable=falseclient.rm.sagaJsonParser=fastjsonclient.rm.tccActionInterceptorOrder=-21448264.tm.commitRetryCount=5client.tm.rollbackRetryCount=5client.tm.defaultGlobalTransactionTimeout=60000client.tm.degradeCheck=falseclient.tm.degradeCheckAllowTimes=10client.tm.degradeCheckPeriod=2000client.tm.interceptorOrder=-21448264.undo.dataValidation=trueclient.undo.logSerialization=jacksonclient.undo.onlyCareUpdateColumns=trueserver.undo.logSaveDays=7server.undo.logDeletePeriod=860000client.undo.logTable=undo_logclient.undo.compress.enable=trueclient.undo.compress.type=zipclient.undo.compress.threshold=64k#For TCC transaction modetcc.fence.logTableName=tcc_fence_logtcc.fence.cleanPeriod=1h#Log rule configuration, for client and serverlog.exceptionRate=100#Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional.# 存储模式改为dbstore.mode=dbstore.lock.mode=filestore.session.mode=file#Used for password encryptionstore.publicKey=#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.store.file.dir=file_store/datastore.file.maxBranchSessionSize=16384store.file.maxGlobalSessionSize=512store.file.fileWriteBufferCacheSize=16384store.file.flushDiskMode=asyncstore.file.sessionReloadReadSize=100#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=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driver# 配置数据库信息store.db.url=jdbc:mysql://zhangbh:3306/seata?useUnicode=true&rewriteBatchedStatements=true# Store数据库账号.db.user=root# 数据库密码store.db.password=rootstore.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.distributedLockTable=distributed_lockstore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.store.redis.mode=singlestore.redis.single.host=127.0.0.1store.redis.single.port=6379store.redis.sentinel.masterName=store.redis.sentinel.sentinelHosts=store.redis.maxConn=10store.redis.minConn=1store.redis.maxTotal=100store.redis.database=0store.redis.password=store.redis.queryLimit=100#Transaction rule configuration, only for the serverserver.recovery.committingRetryPeriod=1000server.recovery.asynCommittingRetryPeriod=1000server.recovery.rollbackingRetryPeriod=1000server.recovery.timeoutRetryPeriod=1000server.maxCommitRetryTimeout=-1server.maxRollbackRetryTimeout=-1server.rollbackRetryTimeoutUnlockEnable=falseserver.distributedLockExpireTime=10000server.xaerNotaRetryTimeout=60000server.session.branchAsyncQueueSize=5000server.session.enableBranchAsyncRemove=false#Metrics configuration, only for the servermetrics.enabled=falsemetrics.registryType=compactmetrics.exporterList=prometheusmetrics.exporterPrometheusPort=9898

4.将配置文件导入nacos执行脚本

sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 0b26125a-4557-4ec5-8afb-8534 -u nacos -w nacos

执行成功后如图所示:

创建数据库
1.存储全局事务信息,创建数据库seata;
https://github.com/seata/seata/tree/develop/script/server/db-- -------------------------------- The script used when storeMode is 'db' ---------------------------------- the table to store GlobalSession dataCREATE 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_status_gmt_modified` (`status` , `gmt_modified`),  KEY `idx_transaction_id` (`transaction_id`)) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;-- the table to store BranchSession dataCREATE 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 = InnoDBDEFAULT CHARSET = utf8mb4;-- the table to store lock dataCREATE 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),  `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',  `gmt_create`     DATETIME,  `gmt_modified`   DATETIME,  PRIMARY KEY (`row_key`),  KEY `idx_status` (`status`),  KEY `idx_branch_id` (`branch_id`),  KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `distributed_lock`(  `lock_key`       CHAR(20) NOT NULL,  `lock_value`     VARCHAR(20) NOT NULL,  `expire`         BIGINT,  primary key (`lock_key`)) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
2.在每个业务库中创建undo_log表,用于存储本地事务回滚信息
CREATE TABLE `undo_log` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `branch_id` bigint(20) NOT NULL,  `xid` varchar(100) NOT NULL,  `context` varchar(128) NOT NULL,  `rollback_info` longblob NOT NULL,  `log_status` int(11) NOT NULL,  `log_created` datetime NOT NULL,  `log_modified` datetime NOT NULL,  PRIMARY KEY (`id`),  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
启动服务
/bin/seata-server.bat
集成Client使用-项目配置pom依赖
<dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>            <version>2021.1</version>            <exclusions>                <exclusion>                    <groupId>io.seata</groupId>                    <artifactId>seata-spring-boot-starter</artifactId>                </exclusion>            </exclusions>        </dependency>        <!--seata starter 采用1.4.2版本-->        <dependency>            <groupId>io.seata</groupId>            <artifactId>seata-spring-boot-starter</artifactId>            <version>${seata.version}</version>        </dependency>
yml配置
# Seata 配置项,对应 SeataProperties 类seata:  enabled: true  # Seata 应用编号,默认为 ${spring.application.name}  application-id: ${spring.application.name}  # Seata 事务组编号,用于 TC 集群名  tx-service-group: ${spring.application.name}-group  # 数据源代理是否自动打开?  enable-auto-data-source-proxy: true  # 数据源代理模式,使用AT模式  data-source-proxy-mode: AT  # 事务组,配置项值为TC集群名,需要与服务端保持一致  service:    # 虚拟组和分组的映射    vgroup-mapping:      account-service-group: default    # 全局事务开关    disable-global-transaction: true  # nacos配置中心集成  config:    type: nacos    nacos:      server-addr: localhost      group: SEATA_GROUP      namespace: 0b26125a-4557-4ec5-8afb-8534      username: nacos      password: nacos  # nacos注册中心整合  registry:    type: nacos    nacos:      server-addr: localhost      group: SEATA_GROUP      namespace: 0b26125a-4557-4ec5-8afb-8534      username: nacos      password: nacos
nacos配置

在相应的yml配置中添加serviceee.vgroup-mapping.account-service-group,值为default

Seata笔记_seata_11

Seata笔记_seata_12

因为service最终通过事务组的映射配置获得.default.grouplist配置项,获得真实的TC服务地址。

使用业务代码

@Globaltransactional即可,如图所示:

Seata笔记_seata_13

上一篇 MinIO笔记
下一篇 Java 仍然伟大的 7 个原因

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题