【黑马-SpringCloud技术栈】【11】分布式事务-Seata

持续学习&持续更新中…

守破离

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

理论基础

事务的ACID原则

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 分布式服务案例

微服务下单业务,在下单时会调用订单服务,创建订单并写入数据库。然后订单服务调用账户服务和库存服务:

  • 账户服务负责扣减用户余额
  • 库存服务负责扣减商品库存

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

分布式服务的事务问题

在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; CAP定理

1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance (分区容错性)

Eric Brewer 说,分布式系统无法同时满足这三个指标。这个结论就叫做 CAP 定理。

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

CAP定理- Consistency

  • Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; CAP定理- Availability

  • Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

CAP定理-Partition tolerance

  • Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
  • Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

在分布式这种架构下,服务与服务之间是必须网络通信的。既然要走网络通信,那么分区就是不可避免的,因为网络是不稳定的(停电、自然灾害、…)。所以,我们需要在一致性和可用性之间做出一个权衡。

  • 假设要确保一致性,那么node03就需要等待其他几点同步数据过来之后才能被访问,那么此时,就降低了系统的可用性。
  • 假设要确保可用性,那么node03的数据就有可能和其他节点的数据不一致,那么此时,就不能确保系统的一致性。

因此,我们可以使用BASE来平衡一下CAP

; 总结

  • 简述CAP定理内容?
  • 分布式系统节点通过网络连接,一定会出现分区问题(P)
  • 当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足
  • 思考:elasticsearch集群是CP还是AP?
  • ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其它节点,保证数据一致。因此是低可用性,高一致性,属于CP

BASE理论

BASE理论是对CAP的一种解决思路,包含三个思想:

  • Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
  • Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。
  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:

  • AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。
  • CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。

分布式事务模型

  • 解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。
  • 这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  • 总结:解决分布式事务的思想和模型:
  • 全局事务:整个分布式事务
  • 分支事务:分布式事务中包含的每个子系统的事务
  • 最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
  • 强一致思想:各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚

; 初识Seata

Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。

官网地址:http://seata.io/,其中的文档、博客中提供了大量的使用说明、源码分析。

Seata的架构(基本模型)

Seata事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) – 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
  • TM (Transaction Manager) – 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) – 资源管理器:管理分支事务处理的资源(数据库),与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; Seata与微服务的整合

一、部署Seata的tc-server

1.下载

  • 首先我们要下载seata-server包,地址在http://seata.io/zh-cn/blog/download.html

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 2.解压

  • 在非中文目录解压缩这个zip包,其目录结构如下:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

3.修改配置

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
* 内容如下:
registry {
  # tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等
  type = "nacos"

  nacos {
    # seata tc 服务注册到 nacos的服务名称,可以自定义
    application = "seata-tc-server"
    serverAddr = "127.0.0.1:8848"
    group = "DEFAULT_GROUP"
    namespace = ""
    cluster = "SH"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置
  type = "nacos"
  # 配置nacos地址等信息
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}

4.在nacos添加配置

  • 特别注意,为了让tc服务的集群可以共享配置,我们选择了nacos作为统一配置中心。因此服务端配置文件seataServer.properties文件需要在nacos中配好。
  • 格式如下:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  • 配置内容如下:(其中的数据库地址、用户名、密码都需要修改成你自己的数据库信息。)

store.mode=db
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=root
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

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
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000

transport.serialization=seata
transport.compressor=none

metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

5.创建数据库表

  • 特别注意:tc服务在管理分布式事务时,需要记录事务相关数据到数据库中,你需要提前创建好这些表,这些表主要记录全局事务、分支事务、全局锁信息。
  • 新建一个名为seata的数据库,运行如下sql:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS branch_table;
CREATE TABLE branch_table  (
  branch_id bigint(20) NOT NULL,
  xid varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  transaction_id bigint(20) NULL DEFAULT NULL,
  resource_group_id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  resource_id varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  branch_type varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  status tinyint(4) NULL DEFAULT NULL,
  client_id varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  application_data varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  gmt_create datetime(6) NULL DEFAULT NULL,
  gmt_modified datetime(6) NULL DEFAULT NULL,
  PRIMARY KEY (branch_id) USING BTREE,
  INDEX idx_xid(xid) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

DROP TABLE IF EXISTS global_table;
CREATE TABLE global_table  (
  xid varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  transaction_id bigint(20) NULL DEFAULT NULL,
  status tinyint(4) NOT NULL,
  application_id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  transaction_service_group varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  transaction_name varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  timeout int(11) NULL DEFAULT NULL,
  begin_time bigint(20) NULL DEFAULT NULL,
  application_data varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  gmt_create datetime NULL DEFAULT NULL,
  gmt_modified datetime NULL DEFAULT NULL,
  PRIMARY KEY (xid) USING BTREE,
  INDEX idx_gmt_modified_status(gmt_modified, status) USING BTREE,
  INDEX idx_transaction_id(transaction_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

6.启动TC服务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
* 启动成功后,seata-server应该已经注册到nacos注册中心了。
【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 二、微服务集成Seata

1.引入依赖

  • 首先,我们需要在微服务中引入seata依赖:
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-seataartifactId>
    <exclusions>

        <exclusion>
            <artifactId>seata-spring-boot-starterartifactId>
            <groupId>io.seatagroupId>
        exclusion>
    exclusions>
dependency>

<dependency>
    <groupId>io.seatagroupId>
    <artifactId>seata-spring-boot-starterartifactId>

    <version>${seata.version}version>
dependency>

2.修改配置文件

  • 需要修改application.yml文件,添加一些配置,让微服务通过注册中心找到seata-tc-server:
seata:
  registry:

    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-tc-server
      cluster: SH
  tx-service-group: seata-demo
  service:
    vgroup-mapping:
      seata-demo: SH

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

3.总结

  • 把account-service、order-service、storage-service这三个微服务都整合一下Seata
  • nacos服务名称组成包括?
  • namespace + group + serviceName + cluster
  • seata客户端获取tc的cluster名称方式?
  • 以tx-group-service的值为key到vgroupMapping中查找

动手实践

Seata提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • SAGA模式:长事务模式,有业务侵入

XA模式

XA规范是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。

XA模式原理(基于数据库本身的特性)

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; Seata的XA模式

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

Seata的XA模式做了一些调整,但大体相似:

  • RM一阶段的工作:
  • 注册分支事务到TC
  • 执行分支业务sql但不提交
  • 报告执行状态到TC
  • TC二阶段的工作:
  • TC检测各分支事务执行状态
    • 如果都成功,通知所有RM提交事务
    • 如果有失败,通知所有RM回滚事务
  • RM二阶段的工作:
  • 接收TC指令,提交或回滚事务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

实现XA模式

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

  1. 修改application.yml文件(每个参与事务的微服务都需要配),开启XA模式:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  1. 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  1. 重启服务并测试

; 优缺点

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库本身底层去实现事务

AT模式

AT模式同样是分阶段提交的事务模型,不过却弥补了XA模型中资源锁定周期过长的缺陷。

AT模式原理

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

AT模式一阶段直接提交,不锁定资源,直接提交,利用数据快照实现数据回滚。

  • 阶段一RM的工作:
  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态
  • 阶段二提交时RM的工作:
  • 删除undo-log即可
  • 阶段二回滚时RM的工作:
  • 根据undo-log恢复数据到更新前

; 举例说明

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

AT模式的脏写问题

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 脏写问题的解决—全局锁进行写隔离

  • 使用Seata的事务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  • 不使用Seata的事务

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

实现AT模式

  • lock_table导入到Seata的TC服务【TC】关联的数据库:
DROP TABLE IF EXISTS lock_table;
CREATE TABLE lock_table  (
  row_key varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  xid varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  transaction_id bigint(20) NULL DEFAULT NULL,
  branch_id bigint(20) NOT NULL,
  resource_id varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  table_name varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  pk varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  gmt_create datetime NULL DEFAULT NULL,
  gmt_modified datetime NULL DEFAULT NULL,
  PRIMARY KEY (row_key) USING BTREE,
  INDEX idx_branch_id(branch_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
  • undo_log表导入到微服务【RM】关联的数据库:
DROP TABLE IF EXISTS undo_log;
CREATE TABLE undo_log  (
  branch_id bigint(20) NOT NULL COMMENT 'branch transaction id',
  xid varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
  context varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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 INDEX ux_undo_log(xid, branch_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
* 重启服务并测试

AT模式与XA模式的区别

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
  • XA模式强一致;AT模式最终一致

优缺点

  • AT模式的优点:
  • 一阶段完成直接提交事务,释放数据库资源,性能比较好
  • 利用全局锁实现读写隔离
  • 没有代码侵入,框架自动完成回滚和提交
  • AT模式的缺点:
  • 两阶段之间属于软状态,属于最终一致
  • 框架的快照功能会影响性能,但比XA模式要好很多

TCC模式

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:

  • Try:资源的检测和预留;
  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。

TCC模式原理

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 举例说明

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

TCC的空回滚和业务悬挂

  • 空回滚

【黑马-SpringCloud技术栈】【11】分布式事务-Seata
  • 业务悬挂:
  • 对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂

; 改造account-service,利用TCC实现分布式事务

为什么只改造account-service呢?因为并不是所有事务都适合使用TCC来解决

  • 需求如下
  • 修改account-service,编写try、confirm、cancel逻辑
  • try业务:添加冻结金额,扣减可用金额
  • confirm业务:删除冻结金额
  • cancel业务:删除冻结金额,恢复可用金额
  • 保证confirm、cancel接口的幂等性
  • 允许空回滚
  • 拒绝业务悬挂

业务分析

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:【微服务的业务数据库中】

CREATE TABLE account_freeze_tbl (
  xid varchar(128) NOT NULL,
  user_id varchar(255) DEFAULT NULL COMMENT '用户id',
  freeze_money int(11) unsigned DEFAULT '0' COMMENT '冻结金额',
  state int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
  PRIMARY KEY (xid) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

当然还需要一张account_freeze表:【微服务的业务数据库中】

DROP TABLE IF EXISTS account_freeze_tbl;
CREATE TABLE account_freeze_tbl  (
  xid varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  user_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  freeze_money int(11) UNSIGNED NULL DEFAULT 0,
  state int(1) NULL DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
  PRIMARY KEY (xid) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;

TCC接口的声明:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

Java代码

@LocalTCC
public interface AccountTCCService {

    @TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money")int money);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);
}
@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    public void deduct(String userId, int money) {

        String xid = RootContext.getXID();

        if (freezeMapper.selectById(xid) != null) {

            return;
        }

        accountMapper.deduct(userId, money);

        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {

        String xid = ctx.getXid();

        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {

        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();
        AccountFreeze freeze = freezeMapper.selectById(xid);

        if (freeze == null) {

            freeze = new AccountFreeze();
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            freezeMapper.insert(freeze);
            return true;
        }

        if (freeze.getState() == AccountFreeze.State.CANCEL) {

            return true;
        }

        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());

        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}
@RestController
@RequestMapping("account")
public class AccountController {

    @Autowired

    private AccountTCCService accountService;

    @PutMapping("/{userId}/{money}")
    public ResponseEntity<Void> deduct(@PathVariable("userId") String userId, @PathVariable("money") Integer money){
        accountService.deduct(userId, money);
        return ResponseEntity.noContent().build();
    }
}

优缺点及总结

  • TCC模式的每个阶段是做什么的?
  • Try:资源检查和预留
  • Confirm:业务执行和提交
  • Cancel:预留资源的释放
  • TCC的优点是什么?
  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
  • TCC的缺点是什么?
  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理
  • 并不是所有的事务都适合使用TCC

SAGA模式

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

; 四种模式对比

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

补充—TC服务的高可用和异地容灾

TC服务作为Seata的核心服务,一定要保证高可用和异地容灾。

1.模拟异地容灾的TC集群

计划启动两台seata的tc服务节点:

节点名称ip地址端口号集群名称seata127.0.0.18091SHseata2127.0.0.18092HZ

之前我们已经启动了一台seata服务,端口是8091,集群名为SH。

现在,将seata目录复制一份,起名为seata2

修改seata2/conf/registry.conf内容如下:

registry {
  # tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等
  type = "nacos"

  nacos {
    # seata tc 服务注册到 nacos的服务名称,可以自定义
    application = "seata-tc-server"
    serverAddr = "127.0.0.1:8848"
    group = "DEFAULT_GROUP"
    namespace = ""
    cluster = "HZ"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置
  type = "nacos"
  # 配置nacos地址等信息
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}

进入seata2/bin目录,然后运行命令:

seata-server.bat -p 8092

打开nacos控制台,查看服务列表:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

点进详情查看:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

2.将事务组映射配置到nacos

接下来,我们需要将tx-service-group与cluster的映射关系都配置到nacos配置中心。

新建一个配置:

【黑马-SpringCloud技术栈】【11】分布式事务-Seata

配置的内容如下:

事务组映射关系
service.vgroupMapping.seata-demo=SH

service.enableDegrade=false
service.disableGlobalTransaction=false
与TC服务的通信配置
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
RM配置
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
TM配置
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000

undo日志配置
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
client.log.exceptionRate=100

3.微服务读取nacos配置

接下来,需要修改每一个微服务的application.yml文件,让微服务读取nacos中的client.properties文件:

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP
      data-id: client.properties

重启微服务,现在微服务到底是连接tc的SH集群,还是tc的HZ集群,都统一由nacos的client.properties来决定了。

参考

黑马程序员:SpringCloud微服务技术栈.

本文完,感谢您的关注支持!

Original: https://blog.csdn.net/weixin_44018671/article/details/121762601
Author: lpruoyu
Title: 【黑马-SpringCloud技术栈】【11】分布式事务-Seata

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/655222/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球