今天打算来说一讲 Dubbo 服务远程调用。笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊。后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现。本地消费者无须知道远程服务具体的实现,消费者和提供者经过代理类来进行交互!!java
简单看一段代码回顾一下动态代理:apache
public class MyInvocationHandler implements InvocationHandler{
private Object object;
public MyInvocationHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
public static void main(String[] args) {
MyInvocationHandler handler = new MyInvocationHandler(stu);
// 生成代理类
Student proxy = (Student)Proxy.newProxyInstance(loader, interfaces, handler);
// 经过代理类调用 sayHello 方法
proxy.sayHello(message);
}
复制代码
实现动态代理的核心步骤有两步:
一、自定义实现了 InvocationHandler 接口的 handler 类,并重写 invoke() 方法
二、调用 Proxy.newProxyInstance 方法建立代理类bootstrap
咱们最后在调用 proxy.sayHello() 的时候,代理类会调用 MyInvocationHandler 类的 invoke() 方法,invoke() 方法经过反射机制最终调用 sayHello() 方法。既然对动态代理的核心组成已经了然,接下来咱们就结合这两点分析下 Dubbo 远程服务调用的实现。windows
在 【Dubbo源码阅读系列】之 Dubbo XML 配置加载 中咱们分析了 Dubbo 解析 XML 配置文件的相关流程,其中 <dubbo:service /> 标签会被解析为 ServiceBean 对象。相似的,<dubbo:reference /> 标签会被解析为 ReferenceBean 对象。ReferenceBean 类继承自 ReferenceConfig 类,仔细观察 ReferenceConfig 类不难发现 ReferenceConfig 中存在这样一条调用链。
get() ==> init() ==> createProxy()
不要怀疑...crateProxy() 方法就是咱们今天的主角...更使人激动的是,咱们能够在该方法中找到与前文概括的动态代理实现核心步骤相对应的代码实现:缓存
invoker = refprotocol.refer(interfaceClass, urls.get(0));
复制代码
这里返回的对象为 Invoker 对象。Invoke 类是一个接口类,里面定义了一个 invoke() 方法。return (T) proxyFactory.getProxy(invoker);
复制代码
在这一小节,咱们只须要对代理类建立流程有个大体的印象便可,咱们在后文深刻分析具体流程。bash
invoker = refprotocol.refer(interfaceClass, urls.get(0));
复制代码
熟悉的配方熟悉的料,经过 Dubbo SPI 机制咱们发现这里调用的实际为 RegistryProtocol.refer(),问我为啥?详见:【Dubbo源码阅读系列】之 Dubbo SPI 机制
refprotocol.refer() 流程比较长,先放张时序图让你们有个基本的印象:app
这里简单归纳下上图中的重点内容:jvm
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
复制代码
这里会新建一个 DubboInvoker 对象并返回,咱们会在后文详细分析;在上节中关于 invoker 的建立咱们留了个小尾巴没有讲完。代码以下:ide
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
复制代码
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
复制代码
总体流程比较简单,可是注意看,这里有个很重要的方法:getClient(url)。它是用来干啥的?还记得咱们再服务暴露之远程暴露那一节启动了 Netty 服务端吗?当时留了个关于 Netty 客户端在哪里启动的坑。这里的 getClients() 就是用来开启 Netty 客户端的。return getExchanger(url).connect(url, handler);
复制代码
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
复制代码
核心方法为 Transporters.connect()return getTransporter().connect(url, handler);
复制代码
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
return new NettyClient(url, listener);
}
复制代码
NettyClient 类构造方法会调用父类 AbstractClient 构造方法。核心方法有两个:
终于要开始建立代理类了,回顾下 ReferenceConfig 中 createProxy() 方法最后一句:post
return (T) proxyFactory.getProxy(invoker);
复制代码
Invoker 对象的建立已经在第二小节详细分析过了。那么 proxyFactory 的 getProxy() 到底干了什么呢?实际上这里又借助了 Dubbo SPI 机制的实现。执行流程大体为:
proxyFactory.getProxy(invoker) ==》 StubProxyFactoryWrapper.getProxy(invoker) ==》AbstractProxyFactory.getProxy(invoker) ==》AbstractProxyFactory.getProxy(invoker, generic) ==> JavassistProxyFactory.getProxy(invoker, interfaces)
重点看 JavassistProxyFactory.getProxy() 方法:
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
复制代码
这里有两点值得提一下:
package org.apache.dubbo.common.bytecode;
import com.alibaba.dubbo.rpc.service.EchoService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
import org.apache.dubbo.demo.DemoService;
public class proxy0 implements DC, EchoService, DemoService {
public static Method[] methods;
private InvocationHandler handler;
public proxy0(InvocationHandler var1) {
this.handler = var1;
}
public proxy0() {
}
public String sayHello(String var1) {
Object[] var2 = new Object[]{var1};
Object var3 = this.handler.invoke(this, methods[0], var2);
return (String)var3;
}
public Object $echo(Object var1) {
Object[] var2 = new Object[]{var1};
Object var3 = this.handler.invoke(this, methods[1], var2);
return (Object)var3;
}
}
复制代码
最后啰嗦一下如何在 windows 系统下查看使用 javassist 字节码技术生成的代理类!!
小结:这一小节写的比较水~不过不要紧,意思已经到了!!