深入剖析 Seata TCC 模式:注解与上下文详解
Seata 是一款由阿里巴巴开源的分布式事务解决方案,支持 AT、TCC、Saga 和 XA 等多种事务模式。TCC(Try-Confirm-Cancel)模式因其灵活性和高性能,特别适合需要手动控制事务逻辑的场景。本文将详细介绍 Seata TCC 模式中的注解及其上下文(BusinessActionContext
),并探讨全局事务中常见的 RootContext
与 TCC 模式中使用的 BusinessActionContext
的联系与差异。我们还会深入分析上下文的来源、调用方式以及值的获取过程。
一、Seata TCC 模式的核心注解
在 TCC 模式中,开发者通过注解定义事务的三个阶段(Try、Confirm、Cancel)。这些注解是 TCC 模式的基础,以下是它们的具体作用和用法。
1. @LocalTCC
-
作用:标记一个接口为 TCC 接口,表示该接口包含 TCC 事务逻辑。
-
使用位置:必须定义在接口上,而非实现类。
-
意义 :Seata 会扫描并解析该接口为 TCC 资源(
TCCResource
),以便在全局事务中管理。 -
示例 :
java@LocalTCC public interface TccService { // TCC 方法定义 }
2. @TwoPhaseBusinessAction
-
作用:定义 Try 阶段的方法,并指定 Confirm 和 Cancel 阶段的对应方法。
-
属性 :
name
:TCC 方法的全局唯一标识,用于注册 TCC 资源。commitMethod
:指定二阶段确认方法的名称。rollbackMethod
:指定二阶段回滚方法的名称。
-
使用位置:Try 阶段的方法。
-
示例 :
java@TwoPhaseBusinessAction(name = "insertTcc", commitMethod = "commitTcc", rollbackMethod = "cancelTcc") String insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params);
3. @BusinessActionContextParameter
-
作用 :将 Try 阶段的参数传递到上下文(
BusinessActionContext
),以便在 Confirm 或 Cancel 阶段使用。 -
属性 :
paramName
:参数在上下文中的键名。
-
使用位置:Try 方法的参数。
-
示例 :
javaString insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params);
在 Confirm 或 Cancel 阶段,可通过
context.getActionContext("params")
获取该参数。
4. Confirm 和 Cancel 方法
-
作用 :分别定义二阶段的提交和回滚逻辑,无需额外注解,但需与
@TwoPhaseBusinessAction
中的commitMethod
和rollbackMethod
名称一致。 -
参数 :通常接收
BusinessActionContext
对象,用于获取上下文信息。 -
示例 :
javaboolean commitTcc(BusinessActionContext context); boolean cancelTcc(BusinessActionContext context);
二、上下文(BusinessActionContext)的全面解析
在 TCC 模式中,BusinessActionContext
是连接 Try、Confirm 和 Cancel 三个阶段的关键。它存储和传递事务相关信息,以下是其来源、调用方式及内容的详细说明。
1. 上下文的来源
- 创建时机 :当 Try 方法被调用时,Seata 的
TccActionInterceptor
拦截器为该分支事务创建BusinessActionContext
实例。 - 填充过程 :
- 全局事务 ID(XID) :由事务发起方通过
@GlobalTransactional
注解开启全局事务时生成,通过RootContext.getXID()
传播。 - 分支事务 ID(Branch ID) :Try 方法执行时,Seata 的资源管理器(RM)向事务协调者(TC)注册分支事务,TC 返回唯一的
branchId
。 - 用户参数 :通过
@BusinessActionContextParameter
注解指定的参数值被存入上下文。 - 其他元数据 :如
resourceId
(TCC 方法的name
)、应用信息等,由 Seata 自动填充。
- 全局事务 ID(XID) :由事务发起方通过
- 传播机制:上下文通过拦截器和 RPC 调用在分布式系统中传递,确保 Confirm 和 Cancel 阶段能获取 Try 阶段的信息。
2. 如何调用上下文
-
获取方式 :在 Confirm 和 Cancel 方法中,
BusinessActionContext
作为参数直接传入。 -
常用方法 :
getXid()
:获取全局事务 ID。getBranchId()
:获取当前分支事务 ID。getActionContext(String key)
:获取 Try 阶段传递的参数值。getActionName()
:获取 TCC 方法的名称(name
属性)。
-
示例 :
javapublic boolean commitTcc(BusinessActionContext context) { String xid = context.getXid(); long branchId = context.getBranchId(); Map<String, String> params = (Map<String, String>) context.getActionContext("params"); System.out.println("XID: " + xid + ", Branch ID: " + branchId + ", Params: " + params); return true; }
3. 上下文中的值如何获取
- XID :由事务管理器(TM)在全局事务开启时向 TC 注册生成,存储在
RootContext
中,通过线程上下文传播。 - Branch ID :由 RM 在 Try 阶段向 TC 注册分支事务时,TC 返回并填充到
BusinessActionContext
。 - 用户参数 :通过
@BusinessActionContextParameter
在 Try 阶段注入,Seata 序列化后存入上下文。 - 其他信息 :如
resourceId
从@TwoPhaseBusinessAction
的name
获取,应用信息从配置或运行时环境提取。
三、RootContext 与 BusinessActionContext 的差异
Seata 的全局事务管理中,RootContext
和 BusinessActionContext
是两个关键上下文类。以下是它们的对比和联系:
1. RootContext
- 作用:全局事务的线程上下文,存储全局事务的基本信息,适用于所有事务模式(AT、TCC、Saga、XA)。
- 主要方法 :
getXID()
:获取全局事务 ID。getBranchId()
:获取当前线程的分支事务 ID(若存在)。
- 特点 :
- 线程级别,通过
ThreadLocal
实现。 - 不包含 TCC 特有的用户参数或资源信息。
- 在全局事务范围内传播,任何地方都可以通过
RootContext.getXID()
获取 XID。
- 线程级别,通过
- 使用场景:通常在事务发起方或需要检查事务状态时使用。
2. BusinessActionContext
- 作用:TCC 模式专用的上下文对象,连接 Try、Confirm 和 Cancel 阶段。
- 主要方法 :如上所述(
getXid()
、getBranchId()
、getActionContext()
等)。 - 特点 :
- 仅在 TCC 模式中使用,不适用于 AT 或其他模式。
- 包含 TCC 特有的信息,如用户通过
@BusinessActionContextParameter
传递的参数。 - 通过方法参数传递,仅在 Confirm 和 Cancel 阶段可用。
- 使用场景:TCC 的二阶段逻辑中,用于获取 Try 阶段的数据。
3. 差异与联系
- 范围 :
RootContext
是全局的,贯穿整个事务生命周期;BusinessActionContext
是分支事务级别的,仅在 TCC 分支中使用。 - 内容 :
RootContext
只存储 XID 和 Branch ID 等基本信息;BusinessActionContext
额外包含 TCC 的用户参数和资源信息。 - 独特性 :
BusinessActionContext
是 TCC 模式独有的,AT 模式依赖undo_log
而无需显式上下文。 - 联系 :两者都依赖 XID 和 Branch ID 的生成逻辑,
BusinessActionContext
的 XID 来源于RootContext
。
四、上下文的完整示例
以下是一个 TCC 接口的完整实现,展示上下文的使用:
java
@LocalTCC
public interface TccService {
@TwoPhaseBusinessAction(name = "insertTcc", commitMethod = "commitTcc", rollbackMethod = "cancelTcc")
String insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params);
boolean commitTcc(BusinessActionContext context);
boolean cancelTcc(BusinessActionContext context);
}
@Service
public class TccServiceImpl implements TccService {
@Override
public String insert(Map<String, String> params) {
System.out.println("Try phase, params: " + params);
return "success";
}
@Override
public boolean commitTcc(BusinessActionContext context) {
String xid = context.getXid();
long branchId = context.getBranchId();
Map<String, String> params = (Map<String, String>) context.getActionContext("params");
System.out.println("Confirm phase, XID: " + xid + ", Branch ID: " + branchId + ", Params: " + params);
return true;
}
@Override
public boolean cancelTcc(BusinessActionContext context) {
String xid = context.getXid();
long branchId = context.getBranchId();
Map<String, String> params = (Map<String, String>) context.getActionContext("params");
System.out.println("Cancel phase, XID: " + xid + ", Branch ID: " + branchId + ", Params: " + params);
return true;
}
}
五、总结
- TCC 注解 :
@LocalTCC
定义 TCC 接口,@TwoPhaseBusinessAction
指定三阶段方法,@BusinessActionContextParameter
传递参数到上下文。 - 上下文来源 :
BusinessActionContext
由 Seata 在 Try 阶段创建,通过拦截器和 TC 注册填充数据。 - 调用与值获取 :通过
BusinessActionContext
的方法获取 XID、Branch ID 和用户参数。 - RootContext vs. BusinessActionContext:前者是全局事务的线程上下文,后者是 TCC 专用的分支事务上下文,功能和使用场景各有侧重。