开发文档

最近更新时间:2019-04-12 17:17:38

配置 TCC 事务

1. 添加依赖

通过 Maven 引入依赖,在项目的 pom 文件添加下述配置项:

<dependency>
    <groupId>com.tencent.tsf</groupId>
    <artifactId>tcc-transaction-core</artifactId>
    <version>1.10.1.TSF-RELEASE</version>
</dependency>

2. 启用 TCC 事务

在 Spring Cloud 的启动类加入注解 EnableTcc:

@SpringBootApplication
@EnableTcc
public class App {
  public static void main(String[] args) {
      SpringApplication.run(App.class);
  }
}

3. 添加注解

您可以在事务的入口函数上添加 @TsfTcc 注解。建议将注解加在接口定义的方法上。
TsfTcc注解有如下属性:

  • serviceName:事务所属的服务名,必选。
  • type:事务类型,TransactionType.ROOT表示主事务,TransactionType.BRANCH表示子事务,默认值为Root,可选。
  • timeout_ms:事务的超时时间,属性可选,默认为60秒,可选。
  • confirmMethodName:confirm方法名,主事务可选,子事务必选。
  • cancelMethodName:cancel方法名,主事务可选,子事务必选。
  • autoRetry:事务超时后,是否由服务器托管继续自动重试。

定义主事务

主事务函数只需要定义一个事务入口函数即可。type 选择 Root类型;函数抛出 Throwable 异常,返回值没有特殊要求;建议业务将所需的参数都封装成一个对象,只使用这一个对象作为入参即可(所有的参数必须实现 Serializable 接口),一个主事务函数定义的样例如下:

public class MainTransaction {

    @TsfTcc(serviceName = "myTcc", type = TransactionType.ROOT, timeout_ms = 60000)
    public String beginTcc(MyParams params) throws Throwable {
        //这里写业务逻辑,调用子事务函数
    }
}

定义子事务

一个完整的子事务需要定义3个方法:TryConfirmCancel
其中只有 Try 方法需要加 TsfTcc 注解,在 TsfTcc 标签中指定对应 Confirm 和 Cancel 方法名即可。

子事务函数定义约束如下:

  • Try、Confirm、Cancel 的前两个参数必须为 String txId 和 long branchId(在主事务调用子事务的时候,这两个参数分别为 null 和0即可),其中 txId 为主事务 ID,全局唯一;branchId为子事务Id,用于区分子事务的父子关系和子事务之间的调用顺序
  • Try 函数返回 Throwable 异常;Try 函数的返回值为 void,TCC 通过异常返回失败结果。
  • Confirm 和 Cancel 函数返回值为 Boolean 类型,操作成功返回 true,操作失败返回 false。

示例:子事务函数定义

public interface SubService {

    @TsfTcc(serviceName = "subService", type = TransactionType.BRANCH, confirmMethodName = "subConfirm", cancelMethodName = "subCancel")
    public void subTry(String txId,long branchId,MyParams params) throws Throwable;

    public boolean subConfirm(String txId,long branchId,MyParams params) throws Throwable;

    public boolean subCancel(String txId,long branchId,MyParams params) throws Throwable;

}

示例:主事务与子事务结合

public class MainTransaction {

    SubService subService;

    @TsfTcc(serviceName = "myTcc", type = TransactionType.ROOT, timeout_ms = 60000)
    public String beginTcc(MyParams params) throws Throwable {
        subService.subTry(null,0,params);
    }
}

检查事务调用

启动服务,从外部调用主事务函数,调用完成之后查看业务日志,一次成功的事务调用日志如下:

14:21:30.470 INFO [main] executeLog - begin transaction: 775025cf-d3e6-3fe8-8c8f-31d44fcde46d
14:21:30.649 INFO [main] executeLog - transaction: 775025cf-d3e6-3fe8-8c8f-31d44fcde46d branch index 1 succ,retry times: 0
14:21:30.652 INFO [main] executeLog - confirm 775025cf-d3e6-3fe8-8c8f-31d44fcde46d branch index: 1, times: 1
14:21:30.653 INFO [main] executeLog - commit 775025cf-d3e6-3fe8-8c8f-31d44fcde46d branch index: 1 succ
14:21:30.653 INFO [main] executeLog - transaction:775025cf-d3e6-3fe8-8c8f-31d44fcde46d confirm succ

子事务访问外部服务

在 SpringCloud 中,您可以通过集成 FeignClient 访问外部服务,再次以上述的子事务为例,假如子事务需要调用一个代金券服务的相关接口,则配置如下:

@FeignClient(value = "couponService")
@RequestMapping(value = "/api/v6/data/couponService")
public interface SubService {

    @TsfTcc(serviceName = "subService", type = TransactionType.BRANCH, confirmMethodName = "subConfirm", cancelMethodName = "subCancel")
    @RequestMapping(value = "/try", method = RequestMethod.POST)
    public void subTry(String txId,long branchId,MyParams params) throws Throwable;

    @RequestMapping(value = "/confirm", method = RequestMethod.POST)
    public boolean subConfirm(String txId,long branchId,MyParams params) throws Throwable;

    @RequestMapping(value = "/cancel", method = RequestMethod.POST)
    public boolean subCancel(String txId,long branchId,MyParams params) throws Throwable;
}

在接口上增加了如下注解:

  • @FeignClient(value = "couponService"),value 的值为外部服务在服务注册中心注册的服务名。
  • @RequestMapping(value = "/api/v6/data/couponService"),value 的值为外部服务的根 URL。

在方法上增加了如下注解:

  • @RequestMapping(value = "/try", method = RequestMethod.POST),value 的值为外部服务的 API 的 URL 和请求方式。

由于分布式事务框架本身会针对异常的调用做重试,业务方在调用事务相关接口(包含主事务和子事务)的时候请关闭请求重试策略。这里以 Spring Cloud 标准的 FeignClient 和 Ribbon 为例,指导如何关闭请求重试策略。

  • FeignClient 默认是不会进行重试的,如果业务自定义了 FeignClient 上面的 retryer,取消自定义配置即可。
  • Ribbon 默认的配置是,当请求失败时,会选择另外一个可用的服务实例进行重试,因此需要手动配置 Ribbon 重试策略。
    1. 为项目创建一个 Spring boot 的标准配置文件 application.properties,如果项目已经有 Spring boot 的 properties 文件,可以不额外创建,直接添加内容即可。
    2. 指定关闭某个服务的自动重试策略。在配置文件中添加配置项:
      couponService.ribbon.MaxAutoRetriesNextServer=0
      couponService.ribbon.MaxAutoRetries=0
      上述配置表示当调用服务名为 couponService 的服务时,关闭 Ribbon 的自动重试,服务名根据业务实际情况进行填写。

分布式事务的超时和重试机制

用户可以在主事务的注释中定义超时时间,默认超时时间为60秒,此时的超时时间为整个事务的超时时间。当事务超时后,事务管理器对事务进行接管,以每5秒一次的重试频率自动触发重试,重试频率逐渐增长,直到600秒。用户可以在控制台上控制中断这一重试过程,也可以手动触发重试。7天后,重试自动终止。

对于未超时的事务,Try 阶段自动重试3次,3次重试不成功自动触发 Cancel 进行回滚。对于 Confirm 或者 Cancel 阶段始终不能执行成功的情况,会重试直到超时。超时后由事务管理器接管。