笔记分享-Seata-分布式事务框架

CSDN话题挑战赛第2期
参赛话题:学习笔记

Seata-分布式事务框架

1.分布式事务问题

1.1本地事务

本地事务 也就是传统的**单机事务 **在传统数据库事务中 必须要满足四个原则:

1.2分布式事务

**分布式事务 **就是指不是在单个服务或单个数据库架构下产生的事务 例如:

  • 跨数据源的分布式事务
  • 跨服务的分布式事务
  • 综合情况

在数据库水平拆分、服务垂直拆分之后 一个业务操作通常要跨多个数据库、服务才能完成

例如电商行业中比较常见的下单付款案例 包括下面几个行为:

  • 创建新订单
  • 扣减商品库存
  • 从用户账户余额扣除金额

完成上面的操作需要访问三个不同的微服务三个不同的数据库

订单的创建、库存的扣减、账户扣款在每一个服务和数据库内是一个本地事务都可以保证ACID原则

但是当我们把三件事情看做一个"业务"时 要满足保证“业务”的原子性 要么所有操作全部成功 要么全部失败

不允许出现部分成功部分失败的现象 这就是分布式系统下的事务

此时ACID难以满足 这是分布式事务要解决的问题

2.理论基础

2.1CAP定理

分布式系统有三个指标。

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

这三个指标不可能同时做到 这个结论就叫做 CAP 定理

2.1.1CAP定理-Consistency(一致性)

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

比如现在包含两个节点 其中的初始数据是一致的 当我们修改其中一个节点的数据时 两者的数据产生了差异

要想保住一致性就必须实现node01到node02的数据同步

2.1.2CAP定理-Availability(可用性)

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

例如有三个节点的集群 访问任何一个都可以及时得到响应

当有部分节点因为网络故障或其它原因无法访问时代表节点不可用

2.1.3CAP定理-PartitionTolerance(分区容错)

Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接 形成独立分区

Tolerance(容错):在集群出现分区时 整个系统也要持续对外提供服务

2.1.4总结

在分布式系统中 系统间的网络不能100%保证健康 一定会有故障的时候 而服务有必须对外保证服务

因此Partition Tolerance(分区容错)不可避免

当节点接收到新的数据变更时,就会出现问题了:

若此时要保证**一致性 **就必须等待网络恢复 完成数据同步后 整个集群才对外提供服务 服务处于阻塞状态 不可用

如果此时要保证**可用性 **就不能等待网络恢复 那node01、node02与node03之间就会出现数据不一致

也就是说 在P一定会出现的情况下 A和C之间只能实现一个

2.2.BASE理论

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

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

2.3.解决分布式事务的思路

分布式事务最大的问题是各个子事务的一致性问题 因此可以借鉴CAP定理和BASE理论 有两种解决思路:

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

任何模式需要在子系统事务之间互相通讯 协调事务状态 即需要一个事务协调者(TC)

这里的子系统事务称为分支事务 有关联的各个分支事务在一起称为全局事务

3.初始Seata

3.1Seata架构

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

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

Seata基于上述架构提供了四种不同的分布式事务解决方案:

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

无论哪种方案 都离不开TC 也就是事务的协调者

3.2部署TC服务

3.2.1下载seata-server包

下载seata-server包,地址在http😕/seata.io/zh-cn/blog/download.html

3.2.2解压并修改配置

修改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 = "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"
  }
}
3.2.3在nacos中添加配置

为了让tc服务的集群可以共享配置 选择了nacos作为统一配置中心

因此服务端配置文件seataServer.properties文件需要在nacos中配好

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&serverTimezone=GMT
store.db.user=root
store.db.password=990914
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功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
3.2.4创建数据库表

c服务在管理分布式事务时 需要记录事务相关数据到数据库中 你需要提前创建好这些表

新建一个名为seata的数据库 这些表主要记录全局事务、分支事务、全局锁信息:

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;
3.2.5启动TC服务

在当前文件夹下进入bin目录并运行sh文件

./seata-server.sh

3.3微服务集成Seata

3.3.1引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <!--版本较低,1.3.0,因此排除-->
        <exclusion>
            <artifactId>seata-spring-boot-starter</artifactId>
            <groupId>io.seata</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--seata starter 采用1.4.2版本-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>
3.3.2修改各个服务的application.yaml配置文件
seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    # 参考tc服务自己的registry.conf中的配置
    type: nacos
    nacos: # tc
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-tc-server # tc服务在nacos中的服务名称
      cluster: SH
  tx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称
  service:
    vgroup-mapping: # 事务组与TC服务cluster的映射关系
      seata-demo: SH

服务注册成功效果图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kPbySre0-1663578765846)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/5560/%E6%88%AA%E5%B1%8F2022-05-27%2019.53.21.png#alt=%E6%88%AA%E5%B1%8F2022-05-27%2019.53.21)]

4.动手实践

4.1XA模式

XA 规范是 X/Open 组织定义的分布式事务处理(DTP Distributed Transaction Processing)标准

XA 规范描述了全局的TM与局部的RM之间的接口 几乎所有主流的数据库都对 XA 规范提供了支持

4.1.1XA模式的两阶段提交

正常情况:

异常情况:

一阶段:

  • 事务协调者(TC)通知每个事物参与者执行本地事务
  • 本地事务执行完成后报告事务执行状态给事务协调者 此时事务不提交 继续持有数据库锁

二阶段:

  • 事务协调者基于一阶段的报告来判断下一步操作

    • 如果一阶段都成功 则通知所有事务参与者 提交事务
    • 如果一阶段任意一个参与者失败 则通知所有事务参与者回滚事务
4.1.2Seata的XA模式

Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:

RM一阶段的工作:

  1. 注册分支事务到TC
  2. 执行分支业务sql但不提交
  3. 报告执行状态到TC

TC二阶段的工作:

  • TC检测各分支事务执行状态

a.如果都成功,通知所有RM提交事务

b.如果有失败 通知所有RM回滚事务

RM二阶段的工作:

  • 接收TC指令 提交或回滚事务
4.1.3.优缺点

XA模式的优点

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

XA模式的缺点

  • 因为一阶段需要锁定数据库资源 等待二阶段结束才释放 性能较差
  • 依赖关系型数据库实现事务
4.1.4实现XA模式

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

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

seata:
  data-source-proxy-mode: XA

2)给发起全局事务的入口方法添加@GlobalTransactional注解 其中OrderServiceImpl中的create方法.

3)重启服务并测试

重启order-service 再次测试 发现无论怎样 三个微服务都能成功回滚

4.2AT模式

4.2.1Seata的AT模型

阶段一RM的工作:

  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前
4.2.2实现AT模式

用一个真实的业务来梳理下AT模式的原理。

比如 现在又一个数据库表 记录用户余额:

idmoney
1100

其中一个分支业务要执行的SQL为:

update tb_account set money = money - 10 where id = 1

AT模式下 当前分支事务执行流程如下:

一阶段:

1)TM发起并注册全局事务到TC

2)TM调用分支事务

3)分支事务准备执行业务SQL

4)RM拦截业务SQL 根据where条件查询原始数据 形成快照

{
    "id": 1, "money": 100
}

5)RM执行业务SQL 提交本地事务 释放数据库锁 此时 money = 90

6)RM报告本地事务状态给TC

二阶段:

1)TM通知TC事务结束

2)TC检查分支事务状态

 a)如果都成功 则立即删除快照

 b)如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}`) 将快照恢复到数据库 此时数据库再次恢复为100

4.2.3XA模式和AT模式的区别

简述AT模式与XA模式最大的区别

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

在多线程并发访问AT模式的分布式事务时 有可能出现脏写问题

解决思路就是引入了全局锁的概念

在释放DB锁之前 先拿到全局锁 避免同一时刻有另外一个事务来操作当前数据

4.2.5.优缺点

AT模式的优点:

  • 一阶段完成直接提交事务 释放数据库资源 性能比较好
  • 利用全局锁实现读写隔离
  • 没有代码侵入 框架自动完成回滚和提交

AT模式的缺点:

  • 两阶段之间属于软状态 属于最终一致
  • 框架的快照功能会影响性能 但比XA模式要好很多

4.3TCC模式

TCC模式与AT模式非常相似 每阶段都是独立事务

不同的是TCC通过人工编码来实现数据恢复 需要实现三个方法:

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

举例 一个扣减用户余额的业务-假设账户A原来余额是100 需要余额扣减30元

  • 阶段一(Try):检查余额是否充足 如果充足则冻结金额增加30元 可用余额扣除30

初识余额:

余额充足 可以冻结:

此时 总金额=冻结金额+可用金额 数量依然是100不变 事务直接提交无需等待其它事务

  • 阶段二(Confirm):假如要提交(Confirm) 则冻结金额扣减30 确认可以提交

不过之前可用金额已经扣减过 只要清除冻结金额就好了:

此时 总金额=冻结金额+可用金额=0+70=70元

  • 阶段二(Canncel):如果要回滚(Cancel) 则冻结金额扣减30 可用余额增加30

需要回滚 那么就要释放冻结金额 恢复可用金额:

4.3.2Seata的TCC模式原理

Seata中的TCC模型架构:

4.3.3.优缺点

TCC模式的每个阶段是做什么的?

  • Try:资源检查和预留
  • Confirm:业务执行和提交
  • Cancel:预留资源的释放

TCC的优点

  • 一阶段完成直接提交事务 释放数据库资源 性能好
  • 相比AT模型 无需生成快照 无需使用全局锁 性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点

  • 有代码侵入 需要人为编写Try、Confirm和Cancel接口太麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况 做好幂等处理
4.3.4事务悬挂和空回滚
4.3.4.1空回滚

当某分支事务的try阶段阻塞时 可能导致全局事务超时而触发二阶段的cancel操作

在未执行try操作时先执行了cancel操作 这时cancel不能做回滚 这就是空回滚

执行cancel操作时 应当判断try是否已经执行 如果尚未执行 则应该空回滚

4.3.4.2业务悬挂

对于已经空回滚的业务 之前被阻塞的try操作恢复 继续执行try 就永远不可能confirm或cancel

事务一直处于中间状态,这就是业务悬挂

执行try操作时 应当判断cancel是否已经执行过了 如果已经执行 应当阻止空回滚后的try操作 避免悬挂

4.3.5实现TCC模式

解决空回滚和业务悬挂问题 必须要记录当前事务状态 是在try、还是cancel

4.3.5.1数据库表定义
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;

其中:

  • xid:是全局事务id
  • freeze_money:用来记录用户冻结金额
  • state:用来记录事务状态

各种业务功能:

  • Try业务:

    • 记录冻结金额和事务状态到account_freeze表
    • 扣减account表可用金额
  • Confirm业务

    • 根据xid删除account_freeze表的冻结记录
  • Cancel业务

    • 修改account_freeze表 冻结金额为0 state为2(CANCEL)
    • 修改account表 恢复可用金额
  • 判断是否空回滚

    • cancel业务中 根据xid查询account_freeze 如果为null则说明try还没做 需要空回滚
  • 避免业务悬挂

    • try业务中 根据xid查询account_freeze 如果已经存在则证明Cancel已经执行 拒绝执行try业务

改造account-service 利用TCC实现余额扣减功能

4.3.5.2声明TCC接口

TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明

在account-service项目中的cn.itcast.account.service包中新建一个接口,声明TCC三个接口:

package cn.itcast.account.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

@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 context);

    boolean cancel(BusinessActionContext context);
}
4.3.5.3编写接口实现类

在account-service服务中的cn.itcast.account.service.impl包下新建一个类 实现TCC接口业务:

package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {

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

    @Override
    @Transactional
    public void deduct(String userId, int money) {
        // 0.获取事务id
        String xid = RootContext.getXID();
        // 1.扣减可用余额
        accountMapper.deduct(userId, money);
        // 2.记录冻结金额,事务状态
        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) {
        // 1.获取事务id
        String xid = ctx.getXid();
        // 2.根据id删除冻结记录
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        // 0.查询冻结记录
        String xid = ctx.getXid();
        AccountFreeze freeze = freezeMapper.selectById(xid);

        // 1.恢复可用余额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        // 2.将冻结金额清零,状态改为CANCEL
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}

4.4SAGA模式

Saga模式是Seata提供的长事务解决方案

  • 一阶段:直接提交本地事务
  • 二阶段:成功则什么都不做 失败则通过编写补偿业务来回滚

优点:

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高
  • 一阶段直接提交事务 无锁 性能好
  • 不用编写TCC中的三个阶段 实现简单

缺点:

  • 软状态持续时间不确定 时效性差
  • 没有锁 没有事务隔离 会有脏写

4.5四种模式对比总结

5.高可用

Seata的TC服务作为分布式事务核心 一定要保证集群的高可用性

高可用模型架构

搭建TC服务集群非常简单 启动多个TC服务 注册到nacos即可

但集群并不能确保100%安全 所以如果要求较高 一般都会做异地多机房容灾

比如一个TC集群在上海 另一个TC集群在杭州:

微服务基于事务组(tx-service-group)与TC集群的映射关系来查找当前应该使用哪个TC集群

当SH集群故障时 只需要将vgroup-mapping中的映射关系改成HZ 则所有微服务就会切换到HZ的TC集群

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啵啵肠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值