1. 概述

Fescar API 分为两大类:High-Level API 和 Low-Level API :

  • High-Level API :用于事务边界定义、控制及事务状态查询。
  • Low-Level API :用于控制事务上下文的传播。

2. High-Level API

2.1 GlobalTransaction

全局事务:包括开启事务、提交、回滚、获取当前状态等方法。

  1. public interface GlobalTransaction {
  2.  
  3. /**
  4. * 开启一个全局事务(使用默认的事务名和超时时间)
  5. */
  6. void begin() throws TransactionException;
  7.  
  8. /**
  9. * 开启一个全局事务,并指定超时时间(使用默认的事务名)
  10. */
  11. void begin(int timeout) throws TransactionException;
  12.  
  13. /**
  14. * 开启一个全局事务,并指定事务名和超时时间
  15. */
  16. void begin(int timeout, String name) throws TransactionException;
  17.  
  18. /**
  19. * 全局提交
  20. */
  21. void commit() throws TransactionException;
  22.  
  23. /**
  24. * 全局回滚
  25. */
  26. void rollback() throws TransactionException;
  27.  
  28. /**
  29. * 获取事务的当前状态
  30. */
  31. GlobalStatus getStatus() throws TransactionException;
  32.  
  33. /**
  34. * 获取事务的 XID
  35. */
  36. String getXid();
  37.  
  38. }

2.2 GlobalTransactionContext

GlobalTransaction 实例的获取需要通过 GlobalTransactionContext:

  1. /**
  2. * 获取当前的全局事务实例,如果没有则创建一个新的实例。
  3. */
  4. public static GlobalTransaction getCurrentOrCreate() {
  5. GlobalTransaction tx = getCurrent();
  6. if (tx == null) {
  7. return createNew();
  8. }
  9. return tx;
  10. }
  11.  
  12. /**
  13. * 重新载入给定 XID 的全局事务实例,这个实例不允许执行开启事务的操作。
  14. * 这个 API 通常用于失败的事务的后续集中处理。
  15. * 比如:全局提交超时,后续集中处理通过重新载入该实例,通过实例方法获取事务当前状态,并根据状态判断是否需要重试全局提交操作。
  16. */
  17. public static GlobalTransaction reload(String xid) throws TransactionException {
  18. GlobalTransaction tx = new DefaultGlobalTransaction(xid, GlobalStatus.UnKnown, GlobalTransactionRole.Launcher) {
  19. @Override
  20. public void begin(int timeout, String name) throws TransactionException {
  21. throw new IllegalStateException("Never BEGIN on a RELOADED GlobalTransaction. ");
  22. }
  23. };
  24. return tx;
  25. }

2.3 TransactionalTemplate

事务化模板:通过上述 GlobalTransaction 和 GlobalTransactionContext API 把一个业务服务的调用包装成带有分布式事务支持的服务。

  1. public class TransactionalTemplate {
  2.  
  3. public Object execute(TransactionalExecutor business) throws TransactionalExecutor.ExecutionException {
  4.  
  5. // 1. 获取当前全局事务实例或创建新的实例
  6. GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
  7.  
  8. // 2. 开启全局事务
  9. try {
  10. tx.begin(business.timeout(), business.name());
  11.  
  12. } catch (TransactionException txe) {
  13. // 2.1 开启失败
  14. throw new TransactionalExecutor.ExecutionException(tx, txe,
  15. TransactionalExecutor.Code.BeginFailure);
  16.  
  17. }
  18.  
  19. Object rs = null;
  20. try {
  21. // 3. 调用业务服务
  22. rs = business.execute();
  23.  
  24. } catch (Throwable ex) {
  25.  
  26. // 业务调用本身的异常
  27. try {
  28. // 全局回滚
  29. tx.rollback();
  30.  
  31. // 3.1 全局回滚成功:抛出原始业务异常
  32. throw new TransactionalExecutor.ExecutionException(tx, TransactionalExecutor.Code.RollbackDone, ex);
  33.  
  34. } catch (TransactionException txe) {
  35. // 3.2 全局回滚失败:
  36. throw new TransactionalExecutor.ExecutionException(tx, txe,
  37. TransactionalExecutor.Code.RollbackFailure, ex);
  38.  
  39. }
  40.  
  41. }
  42.  
  43. // 4. 全局提交
  44. try {
  45. tx.commit();
  46.  
  47. } catch (TransactionException txe) {
  48. // 4.1 全局提交失败:
  49. throw new TransactionalExecutor.ExecutionException(tx, txe,
  50. TransactionalExecutor.Code.CommitFailure);
  51.  
  52. }
  53. return rs;
  54. }
  55.  
  56. }

模板方法执行的异常:ExecutionException

  1. class ExecutionException extends Exception {
  2. // 发生异常的事务实例
  3. private GlobalTransaction transaction;
  4. // 异常编码:
  5. // BeginFailure(开启事务失败)
  6. // CommitFailure(全局提交失败)
  7. // RollbackFailure(全局回滚失败)
  8. // RollbackDone(全局回滚成功)
  9. private Code code;
  10. // 触发回滚的业务原始异常
  11. private Throwable originalException;

外层调用逻辑 try-catch 这个异常,根据异常编码进行处理:

  • BeginFailure (开启事务失败):getCause() 得到开启事务失败的框架异常,getOriginalException() 为空。
  • CommitFailure (全局提交失败):getCause() 得到开启事务失败的框架异常,getOriginalException() 为空。
  • RollbackFailure (全局回滚失败):getCause() 得到开启事务失败的框架异常,getOriginalException() 业务应用的原始异常。
  • RollbackDone (全局回滚成功):getCause() 为空,getOriginalException() 业务应用的原始异常。

3. Low-Level API

3.1 RootContext

事务的根上下文:负责在应用的运行时,维护 XID 。

  1. /**
  2. * 得到当前应用运行时的全局事务 XID
  3. */
  4. public static String getXID() {
  5. return CONTEXT_HOLDER.get(KEY_XID);
  6. }
  7.  
  8. /**
  9. * 将全局事务 XID 绑定到当前应用的运行时中
  10. */
  11. public static void bind(String xid) {
  12. if (LOGGER.isDebugEnabled()) {
  13. LOGGER.debug("bind " + xid);
  14. }
  15. CONTEXT_HOLDER.put(KEY_XID, xid);
  16. }
  17.  
  18. /**
  19. * 将全局事务 XID 从当前应用的运行时中解除绑定,同时将 XID 返回
  20. */
  21. public static String unbind() {
  22. String xid = CONTEXT_HOLDER.remove(KEY_XID);
  23. if (LOGGER.isDebugEnabled()) {
  24. LOGGER.debug("unbind " + xid);
  25. }
  26. return xid;
  27. }
  28.  
  29. /**
  30. * 判断当前应用的运行时是否处于全局事务的上下文中
  31. */
  32. public static boolean inGlobalTransaction() {
  33. return CONTEXT_HOLDER.get(KEY_XID) != null;
  34. }

High-Level API 的实现都是基于 RootContext 中维护的 XID 来做的。

应用的当前运行的操作是否在一个全局事务的上下文中,就是看 RootContext 中是否有 XID。

RootContext 的默认实现是基于 ThreadLocal 的,即 XID 保存在当前线程上下文中。

Low-Level API 的两个典型的应用场景:

1. 远程调用事务上下文的传播

远程调用前获取当前 XID:

  1. String xid = RootContext.getXID();

远程调用过程把 XID 也传递到服务提供方,在执行服务提供方的业务逻辑前,把 XID 绑定到当前应用的运行时:

  1. RootContext.bind(rpcXid);

2. 事务的暂停和恢复

在一个全局事务中,如果需要某些业务逻辑不在全局事务的管辖范围内,则在调用前,把 XID 解绑:

  1. String unbindXid = RootContext.unbind();

待相关业务逻辑执行完成,再把 XID 绑定回去,即可实现全局事务的恢复:

  1. RootContext.bind(unbindXid);