文章目录
1 dubbo中Invoker的重要性
上篇文章《【dubbo源码解析】— dubbo的服务注册与发现机制底层原理探析》讲解到在不加入注册中心逻辑之前,dubbo中消费端@Reference注解标注的bean进行方法调用的过程如下图所示:
假如你clone下来我上篇文章对应的代码,并跟了一下源码的话,那你肯定知道在引入注册中心后:
- (1)其实在服务端
RegistryProtocol
对象将Invoker交给具体的Protocol进行服务暴露时,传入的并不是原始的Invoker,而是将其包装成了一个InvokerDelegete对象(也是一个Invoker)
则当服务端被调用时,其实是先调用到这个InvokerDelegete对象的invoke方法—>然后再在InvokerDelegete对象内部调用原始Invoker的invoke方法 —> 最后才真正调用的ref对象的具体方法
- (2)而在消费端其实
也不是一下就
由具体的Protocol获取到【可以调用远程服务的对象】
,然后将其和【url】【interface】封装为一个Invoker。 其实在消费端也是在最外层包了一个XXXClusterInvoker。
也就是说在消费端@Reference注解标注的bean进行方法调用时,会先调用XXXClusterInvoker的invoke方法—》然后再调用由具体的Protocol获取到包装了
【可以调用远程服务的对象】
的Invoker的invoke方法 —》然后调用远程服务。
实际上我们真实dubbo开发中遇到的过滤器链
、负载均衡
、容错
等组件的底层逻辑的具体实现都被封装成了一个个Invoker对象,串联在dubbo的整个RPC链条中 , 画个dubbo的RPC调用简图大致如下:
由此可知在dubbo中Invoker对象,非常重要!!!
2 dubbo RPC链条中代理对象的底层逻辑
2.1 以之前的文章为基础实现一个简单的dubbo服务端+消费端
如果看过前面几篇文章的话,相信对于下面的代码你肯定不会陌生:
服务端
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//代理
ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
//注册中心服务--zk
final URL registryUrl = URL.valueOf("registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper");
//支持的协议:dubbo,http,hessian,rmi
URL serviceUrl = URL.valueOf("rmi://127.0.0.1:9001/com.nrsc.service.InvokerDemoService" + "?proxy=jdk"); //动态代理
@Test
public void serverRpc() throws IOException {
InvokerDemoService service = new InvokerDemoServiceImpl("yoyo");
serviceUrl = serviceUrl.setPort(9001);
URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl.toFullString());
//暴露服务
Invoker<InvokerDemoService> serviceInvoker = proxy.getInvoker(service, InvokerDemoService.class, newRegistryUrl);
Exporter<InvokerDemoService> exporter = protocol.export(serviceInvoker);
System.out.println("server 启动协议:" + serviceUrl.getProtocol());
// 保证服务一直开着
System.in.read();
exporter.unexport();
}
消费端
@Test
public void clientRpc() {
Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
//获取【可以调用远程服务的对象】的代理对象,当调用此对象的方法时会走代理逻辑,调用referInvoker的invoke方法
InvokerDemoService service = proxy.getProxy(referInvoker);
String result = service.sayHello(registryUrl.getProtocol() + "调用");
System.out.println(result);
}
2.2 代理对象的底层逻辑
上面的代码消费端和服务端肯定是可以调通的,这里有兴趣的可以自己测试一下。但是下面的这行代码的底层逻辑究竟是什么呢?
String result = service.sayHello(registryUrl.getProtocol() + "调用");
其实它底层的逻辑,我在《【dubbo源码解析】— dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析》这篇文章里讲过。以动态代理为例,当使用代理对象service调用方法时,它会走InvokerInvocationHandler中的invoke方法(动态代理的逻辑) —> 然后在该方法里调用Invoker的invoke方法,其源码如下:
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
//调用Invoker的invoke方法
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
这里可以注意到,其实它底层就是将service调用的方法(method)和具体的参数(args)封装成一个RpcInvocation对象,然后拿着这个封装的参数来调用Invoker的invoke方法(再底层又用到了反射,有兴趣的可以看一下,挺简单的)。
实际上这个RpcInvocation对象的构建不仅可以使用method和args,它还有如下构造方法
由此可知我们其实可以直接构建一个RpcInvocation对象,然后拿着它直接调用Invoker的invoke方法,来直接拉通整个dubbo的RPC调用流程 。
2.3 跳过代理对象利用直接构造的RpcInvocation对象拉通整个RPC链条
以上图中黄色箭头指定的构造方法构造RpcInvocation对象,并进行消费端调用的demo:
@Test
public void clientInvoker() {
Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
//直接构造RpcInvocation对象
Invocation invocation = new RpcInvocation(
"sayHello", new Class<?>[]{String.class}, new Object[]{"invoke直接调用"}, null);
//拿着构造的RpcInvocation对象直接调用referInvoker的invoke方法
Result result2 = referInvoker.invoke(invocation);
System.out.println(result2.getValue());
}
测试结果如下:
3 dubbo中Invoker嵌套调用底层原理
dubbo中Invoker的嵌套调用底层原理基本如下 —过滤器链
、负载均衡
、容错机制
等其底层逻辑都是按照下面的逻辑被封装嵌套到dubbo的整个RPC链条中。
这里简单提供一个dubbo的Invoker嵌套调用示例:
private Invocation invocation = new RpcInvocation(
"sayHello", new Class<?>[]{String.class}, new Object[]{"invoke调用"}, null);
@Test
public void clientInvokerWrapper() throws IOException {
//获取包装了【可以调用远程服务的对象】的Invoker
Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
//对包装了【可以调用远程服务的对象】的Invoker再嵌套一个Invoker
Invoker<InvokerDemoService> wrapperInvoker = new Invoker<InvokerDemoService>() {
private Invoker<InvokerDemoService> invoker = referInvoker;
@Override
public Class<InvokerDemoService> getInterface() {
return invoker.getInterface();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
System.out.println("invoker嵌套调用");
return invoker.invoke(invocation);
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return false;
}
@Override
public void destroy() {
}
};
//拿着invocation调用最外层Invoker的invoke方法,由于最外层的Invoker里有包装了【可以调用远程服务的对象】的Invoker,
//所以可以现在最外层的Invoker的invoke方法里先做些事情再调用包装了【可以调用远程服务的对象】的Invoker的invoke方法
Result result3 = wrapperInvoker.invoke(invocation);
System.out.println(result3.getValue());
}
简单看一下测试结果:
4 dubbo的RPC调用简图最终效果
end!