1. 概述

本文分享 Dubbo 支持

TCC-Transaction 通过 Dubbo 隐式传参的功能,避免自己对业务代码的***。可能有同学不太理解为什么说 TCC-Transaction 对业务代码有一定的***性,一起来看个代码例子:

public interface CapitalTradeOrderService {
   String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);
}
  • 代码来自 tcc-transaction-http-sample 。声明远程调用时,增加了参数 TransactionContext。当然你也可以通过自己使用的远程调用框架做一定封装,避免***。

如下是对 Dubbo 封装了后,Dubbo Service 方法的例子:

public interface CapitalTradeOrderService {

   @Compensable
   String record(CapitalTradeOrderDto tradeOrderDto);

}
  • 代码来自 http-transaction-dubbo-sample 。是不是不需要传入参数 TransactionContext。当然,注解是肯定需要的,否则 TCC-Transaction 怎么知道哪些方法是 TCC 方法。

TCC-Transaction 通过 Dubbo Proxy 的机制,实现 @Compensable 属性自动生成,增加开发体验,也避免出错。


Dubbo 支持( Maven 项目 tcc-transaction-dubbo ) 整体代码结构如下:

  • proxy

  • context

我们分成两个小节分享这两个包实现的功能。

笔者暂时对 Dubbo 了解的不够深入,如果有错误的地方,还烦请指出,谢谢。

你行好事会因为得到赞赏而愉悦 
同理,开源项目贡献者会因为 Star 而更加有动力 
为 TCC-Transaction 点赞!传送门

ps:笔者假设你已经阅读过《tcc-transaction 官方文档 —— 使用指南1.2.x》。

2. Dubbo 代理

将 Dubbo Service 方法上的注解 @Compensable ,自动生成注解的 confirmMethodcancelMethodtransactionContextEditor 属性,例子代码如下:

@Compensable(propagation=Propagation.SUPPORTS, confirmMethod="record", cancelMethod="record", transactionContextEditor=DubboTransactionContextEditor.class)
public String record(RedPacketTradeOrderDto paramRedPacketTradeOrderDto) {
   // ... 省略代码
}
  • 该代码通过 Javassist 生成的 Proxy 代码的示例。

  • propagation=Propagation.SUPPORTS :支持当前事务,如果当前没有事务,就以非事务方式执行。为什么不使用 REQUIRED ?如果使用 REQUIRED 事务传播级别,事务恢复重试时,会发起新的事务。

  • confirmMethodcancelMethod 使用和 try 方法相同方法名本地发起远程服务 TCC confirm / cancel 阶段,调用相同方法进行事务的提交或回滚。远程服务的 CompensableTransactionInterceptor 会根据事务的状态是 CONFIRMING / CANCELLING 来调用对应方法。

  • transactionContextEditor=DubboTransactionContextEditor.class,使用 Dubbo 事务上下文编辑器,在「3. Dubbo 事务上下文编辑器」详细分享。

Dubbo Service Proxy 提供了两种生成方式:

  • JavassistProxyFactory,基于 Javassist 方式

  • JdkProxyFactory,基于 JDK 动态代理机制

这块内容我们不拓展开,感兴趣的同学点击如下文章:

  • 《Dubbo学习-理解动态代理》

  • 《Dubbo 作者博客 —— 动态代理方案性能对比》

  • 《Dubbo原理解析-代理之Javassist生成的伪代码》

  • 《Dubbo的服务暴露细节》

Dubbo 的 Invoker 模型是非常关键的概念,看下图:

2.1 JavassistProxyFactory

2.1.1 Javassist

Javassist 是一个开源的分析、编辑和创建 Java 字节码的类库。通过使用Javassist 对字节码操作可以实现动态 ”AOP” 框架。

关于 Java 字节码的处理,目前有很多工具,如 bcel,asm( cglib只是对asm又封装了一层 )。不过这些都需要直接跟虚拟机指令打交道。

Javassist 的主要的优点,在于简单,而且快速,直接使用 Java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

  • 粗略一看,可能不够形象,下面我们通过看 TCC-Transaction 如何使用来理解理解。

  • 《Java学习之javassist 》

  • 《Javassist 字节码操作》

2.1.2 TccJavassistProxyFactory

org.mengyun.tcctransaction.dubbo.proxy.javassist.TccJavassistProxyFactory,TCC Javassist 代理工厂。实现代码如下:

public class TccJavassistProxyFactory extends JavassistProxyFactory {

   @SuppressWarnings("unchecked")
   public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
       return (T) TccProxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
   }

}
  • 项目启动时,调用 TccJavassistProxyFactory#getProxy(...) 方法,生成 Dubbo Service 调用 Proxy。

  • com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler,Dubbo 调用处理器,点击连接查看代码。

2.1.3 TccProxy & TccClassGenerator

org.mengyun.tcctransaction.dubbo.proxy.javassist.TccProxy,TCC Proxy 工厂,生成 Dubbo Service 调用 Proxy 。笔者认为,TccProxy 改成 TccProxyFactory 更合适,原因在下文。

org.mengyun.tcctransaction.dubbo.proxy.javassist.TccClassGenerator,TCC 类代码生成器,基于 Javassist 实现。

案例

一个 Dubbo Service,TccProxy 会动态生成两个类:

  • Dubbo Service 调用 Proxy

  • Dubbo Service 调用 ProxyFactory,生成对应的 Dubbo Service Proxy

例如 Dubbo Service 接口如下:

public interface RedPacketTradeOrderService {

   @Compensable
   String record(RedPacketTradeOrderDto tradeOrderDto);
}

生成 Dubbo Service 调用 ProxyFactory 如下 :

public class TccProxy3 extends TccProxy implements TccClassGenerator.DC {
 public Object newInstance(InvocationHandler paramInvocationHandler) {
   return new proxy3(paramInvocationHandler);
 }
}
  • TccProxy 提供 #newInstance(handler) 方法,创建 Proxy,所以笔者认为,TccProxy 改成 TccProxyFactory 更合适。

  • org.mengyun.tcctransaction.dubbo.proxy.javassist.TccClassGenerator.DC 动态生成类标记,标记该类由 TccClassGenerator 生成的。

生成 Dubbo Service 调用 Proxy 如下 :

public class proxy3 implements TccClassGenerator.DC, RedPacketTradeOrderService, EchoService {

   public static Method[] methods;
   private InvocationHandler handler;

   public proxy3() {}

   public proxy3(InvocationHandler paramInvocationHandler) {
       this.handler = paramInvocationHandler;
   }

   @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = DubboTransactionContextEditor.class)
   public String record(RedPacketTradeOrderDto paramRedPacketTradeOrderDto) {
       Object[] arrayOfObject = new Object[1];
       arrayOfObject[0] = paramRedPacketTradeOrderDto;
       Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);
       return (String) localObject;
   }

   public Object $echo(Object paramObject) {
       Object[] arrayOfObject = new Object[1];
       arrayOfObject[0] = paramObject;
       Object localObject = this.handler.invoke(this, methods[1], arrayOfObject);
       return (Object) localObject;
   }
}
  • com.alibaba.dubbo.rpc.service.EchoService,Dubbo Service 回声服务接口,用于服务健康检查,Dubbo Service 默认自动实现该接口,点击连接查看代码。

  • org.mengyun.tcctransaction.dubbo.proxy.javassist.TccClassGenerator.DC 动态生成类标记,标记该类由 TccClassGenerator 生成的。

  • 生成 Dubbo Service 调用 Proxy 如下 :

  • public class proxy3 implements TccClassGenerator.DC, RedPacketTradeOrderService, EchoService {

       public static Method[] methods;
       private InvocationHandler handler;

       public proxy3() {}

       public proxy3(InvocationHandler paramInvocationHandler) {
           this.handler = paramInvocationHandler;
       }

       @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = DubboTransactionContextEditor.class)
       public String record(RedPacketTradeOrderDto paramRedPacketTradeOrderDto) {
           Object[] arrayOfObject = new Object[1];
           arrayOfObject[0] = paramRedPacketTradeOrderDto;
           Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);
           return (String) localObject;
       }

       public Object $echo(Object paramObject) {
           Object[] arrayOfObject = new Object[1];
           arrayOfObject[0] = paramObject;
           Object localObject = this.handler.invoke(this, methods[1], arrayOfObject);
           return (Object) localObject;
       }
    }
  • com.alibaba.dubbo.rpc.service.EchoService,Dubbo Service 回声服务接口,用于服务健康检查,Dubbo Service 默认自动实现该接口,点击连接查看代码。

  • org.mengyun.tcctransaction.dubbo.proxy.javassist.TccClassGenerator.DC 动态生成类标记,标记该类由 TccClassGenerator 生成的。

生成 Dubbo Service 调用 Proxy 如下 :

public class proxy3 implements TccClassGenerator.DC, RedPacketTradeOrderService, EchoService {

   public static Method[] methods;
   private InvocationHandler handler;

   public proxy3() {}

   public proxy3(InvocationHandler paramInvocationHandler) {
       this.handler = paramInvocationHandler;
   }

   @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = DubboTransactionContextEditor.class)
   public String record(RedPacketTradeOrderDto paramRedPacketTradeOrderDto) {
       Object[] arrayOfObject = new Object[1];
       arrayOfObject[0] = paramRedPacketTradeOrderDto;
       Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);
       return (String) localObject;
   }

   public Object $echo(Object paramObject) {
       Object[] arrayOfObject = new Object[1];
       arrayOfObject[0] = paramObject;
       Object localObject = this.handler.invoke(this, methods[1], arrayOfObject);
       return (Object) localObject;
   }
}