分布式事务 TCC-Transaction 源码分析 —— Dubbo 支持
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
,自动生成注解的 confirmMethod
、cancelMethod
、transactionContextEditor
属性,例子代码如下:
@Compensable(propagation=Propagation.SUPPORTS, confirmMethod="record", cancelMethod="record", transactionContextEditor=DubboTransactionContextEditor.class)
public String record(RedPacketTradeOrderDto paramRedPacketTradeOrderDto) {
// ... 省略代码
}
该代码通过 Javassist 生成的 Proxy 代码的示例。
propagation=Propagation.SUPPORTS
:支持当前事务,如果当前没有事务,就以非事务方式执行。为什么不使用 REQUIRED ?如果使用 REQUIRED 事务传播级别,事务恢复重试时,会发起新的事务。confirmMethod
、cancelMethod
使用和 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;
}
}
com.alibaba.dubbo.rpc.service.EchoService
,Dubbo Service 回声服务接口,用于服务健康检查,Dubbo Service 默认自动实现该接口,点击连接查看代码。org.mengyun.tcctransaction.dubbo.proxy.javassist.TccClassGenerator.DC
动态生成类标记,标记该类由 TccClassGenerator 生成的。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 生成的。©著作权归作者所有:来自51CTO博客作者mb5ff80520dfa04的原创作品,如需转载,请注明出处,否则将追究法律责任
更多相关文章
- 怎么提升写代码的能力
- 函数式编程思维在三行代码情书中的应用
- jQuery做一个漂亮的下拉框,用十几行代码就够了
- 【前端词典】从 returnWeekday() 谈 if() 语句代码优化
- 25条很棒的Python一行代码,建议收藏!
- 什么,3行Python代码就能获取海量数据?
- NBA投篮数据可视化,4行代码就能实现!
- 刷爆全网的动态条形图,原来5行Python代码就能实现!
- 牛逼!一行代码让 pandas 的 apply 速度飙到极致!
随机推荐