SAGA 模式 Spring Free 开发

最近更新时间: 2025-01-15 17:01:00

操作场景

该任务指导您在 Saga 模式下进行 Spring Free 开发。 手动启动 Saga 事务,需要用户自行编写 Execute,Compensate 接口的实现,并保证这两个方法的幂等性

准备工作

  • 参考 准备工作 文档,完成环境配置和开发前准备。
  • 参考 快速部署 文档,执行 FMT 初始化脚本.

接入步骤

1. 引入 DTF SDK

通过以下方式引入 Spring Free 版本的 DTF SDK。

<dependency>
    <groupId>com.tencent.cloud</groupId>
    <artifactId>dtf-core</artifactId>
    <version>{dtf.version}</version>
</dependency>

说明:

version 填写 Release Note 中最新版本的即可。

2. 客户端配置

在客户端中,使用以下方式添加基本配置。

DtfEnv.setServer(String server);
DtfEnv.setSecretId(String secretId);
DtfEnv.setSecretKey(String secretKey);
DtfEnv.addTxmBroker(String groupId, String txmBrokerList);
配置项数据类型必填默认值描述
groupIdString用户的事务分组 ID
txmBrokerListStringTC 集群节点列表
secretIdString用户的SecretID
secretKeyString用户的SecretKey
serverString客户端服务标识,一个事务分组下,同一服务需要使用相同的标识
dtf.env.fmtBooleantrue启动时会对 DB 进行大量初始化工作,若不需使用 fmt 建议禁用

说明:

通常情况下,仅需要在 dtf.env.groups 下配置一个事务分组。

3. 注册 Saga

使用以下接口,手动注册 Saga 接口。

private static void registerSaga() {
    ITransferService transferService = new TransferService();
    // debit 的 compensate
    SagaRegistry.register("com.tencent.cloud.dtf.demo.transfer.TransferService.debit",
            SagaRegistry.SAGA.getInstance(transferService, "compensateDebit"));
    // credit 的 compensate
    SagaRegistry.register("com.tencent.cloud.dtf.demo.transfer.TransferService.credit",
            SagaRegistry.SAGA.getInstance(transferService, "compensateCredit"));
}

注册方式可以参考 SagaRegistry.register() 和 SAGA.getInstance() 的 javadoc。

4. 启动分布式事务服务

通过以下方式启动分布式事务服务。

public static void main(String[] args) {
        // 设置参数
        initEnv();
        // 注册 Saga
        registerSaga();
        // 启动分布式事务客户端
        DtfClient.start();
    }

5. 开启主事务

通过以下接口开启主事务。

主事务注解方法正常返回时提交主事务,在抛出异常时进行回滚。

Long txId = DtfTransaction.begin(Integer timeout);
// 或
Long txId = DtfTransaction.begin(String groupId, Integer timeout);
参数数据类型必填默认值描述
timeoutInteger60 * 1000事务超时时间(所有 Try 阶段),单位:毫秒
groupIdStringDtfEnv.addTxmBroker 仅配置了一个事务分组时,使用该值主事务的事务分组 ID

6. 开启分支事务

通过以下接口开启分支事务。

Long branchId1 = DtfTccBranch.begin("name", new Object[] {null, null, this.to, this.amount});

分支事务注解支持的参数包括:

参数数据类型必填默认值描述
nameString分支事务名称,请在同一事务分组
参数列表Array <String>参数列表,前两个参数固定为 null

7. Compensate 操作

一个分支事务中,需要包含 Execute 和 Compensate 两个部分。可以使用 步骤5 中的默认值简化配置。

  • 分支事务的 Execute 和 Compensate 方法的参数保持一致
  • 分支事务的 Execute 和 Compensate 方法的前两个参数固定为 Long txIdLong branchId

Execute 方法:

  • 本地调用 Execute 方法时 txIdbranchId 参数传 null,其他参数正常传递。
  • 返回值为业务逻辑需要的返回值。

Compensate 方法:

  • 返回值固定为 Boolean 类型。
  • 仅在返回 true 时视为分支事务 Compensate 成功。
  • 返回 false抛出异常时,视为分支事务 Compensate 失败。
void debit(Long txId, Long branchId, Account account, int amount) throws Exception;

boolean compensateDebit(Long txId, Long branchId, Account account, int amount);

8. 远程请求(自行处理)

  • 上游处理: 需要从上下文中提取 txIdgroupIdlastBranchId 三个内容传递到下游。

     txId: DtfContextHolder.get().getTxId();
     groupId: DtfContextHolder.get().getGroupId();
     lastBranchId: DtfContextHolder.get().getBranchIdStack().peek();

说明:

建议放到下列 Header 的 key 中,下游可以通过 DTF SDK 自行注入。

   ClientConstant.HTTP_HEADER.TX_ID: txId
   ClientConstant.HTTP_HEADER.GROUP_ID: groupId
   ClientConstant.HTTP_HEADER.LAST_BRANCH_ID: lastBranchId
  • 下游处理: 下游可以使用以下方法将从上游传递的三个变量绑定到本地,重新开启全局事务。

     DtfContextHolder.bind(txId, lastBranchId, groupId);