目录
简介
过滤器 (Filter)
负载均衡接口 (LoadBalance)
容错接口 (Cluster)
源码分析
1. 获取Invoker过程
2. 获取动态代理对象proxy
3. 最后调用此动态代理对象的invoke方法
过滤器、容错组件、负载均衡之间的关系
简介
本篇将围绕Dubbo消费端的主流程进行讲解,其中会涉及到过滤器、容错、复杂均衡进行重点分析。
首先,我们需要认识几个重要的接口 Filter、Cluster、LoadBalance,分别对应过滤器、容错、复杂均衡。下面会这几个接口进行初步介绍。
过滤器 (Filter)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.SPI;
@SPI
public interface Filter {
Result invoke(Invoker<?> var1, Invocation var2) throws RpcException;
}
一看就是Dubbo SPI的设计思想,实现类如下:
而他的配置文件如下:
负载均衡接口 (LoadBalance)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcException;
import java.util.List;
@SPI("random")
public interface LoadBalance {
@Adaptive({"loadbalance"})
<T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}
默认策略是随机策略,而他的实现类和对应的key为:
容错接口 (Cluster)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcException;
@SPI("failover")
public interface Cluster {
@Adaptive
<T> Invoker<T> join(Directory<T> var1) throws RpcException;
}
对应的实现类,以及key-value结构如下:
源码分析
客户端测试代码:
@Test
public void clientRpc() throws IOException
{
Protocol protocol = protocolLoader.getAdaptiveExtension();
//代理
ProxyFactory proxy = proxyLoader.getAdaptiveExtension();
//消费服务
Invoker<DemoService> referInvoker = protocol.refer(DemoService.class, registryUrl);
DemoService service = proxy.getProxy(referInvoker);
for (int i = 0;i < 5;i++){
String result = service.sayHello(registryUrl.getProtocol()+"调用");
System.out.println(result);
}
// 保证服务一直开着
// System.in.read();
}
1. 获取Invoker过程
我们知道,无论是 ProxyFactory 还是 Protocol, 他两都是一个静态代理类,那么获取Invoker对象实际调用到的就是静态代理类的refer方法:
进入到RegistryProtocol类的getInvoker方法。其实,就是根据注册中心的registryURL和 Interface信息到注册中心中进行注册与订阅操作。我们使用的是zookeeper,那也就是zookeeper的发布与定于操作,不是本文的重点,在分析zookeeper的时候会重点分析。
其实,我们获取的Invoker对象,是一个容错对象 invoker
2. 获取动态代理对象proxy
因为proxy是一个静态代理对象,因此会调用此对象的getProxy方法
3. 最后调用此动态代理对象的invoke方法
随着debug的深入,我们发现它首先调用的是容错组件的子类:
而在容错组件的深入过程中,我们发现在容错组件的调用过程中,我们又调用到了负载均衡的策略:
最后继续走到方法的最后一行,又调用了doInvoke方法。其实,这就是负载均衡的详细逻辑
继续debug,我们发现它进入了 ProtocolFilterWrapper 内部,并且调用了invoke方法。而当前方法很明显是一个链式调用。
继续debug,我发现他们调用调用了很多的过滤器Filter的实现类,其中就有我们自己定义的过滤器实现类。最后,它才会调用到实际的动态代理对象的invoke方法,进行网络调用:
我们发现,它正常调用到了服务端的信息,并且返回预期的结果。
过滤器、容错组件、负载均衡之间的关系
1. 其实,普通的RPC调用,完全可以理解成Invoker对象的直接调用。 也就是服务端直接把要暴露的服务封装成 Invoker对象进行暴露,消费端直接根据URL进行调用,这是最简单、最原始的调用
消费端 Invoker.invoke ------> 网络 ---> 服务端 Invoker.invoker ----> 实际暴露的业务
2. Dubbo可以看做是对RPC进行了增强
消费端 invoker.invoke ------>容错策略--->网络---->服务端 invoker.invoke--->ref 服务
为了统一接口,dubbo对外暴露的就是一个Invoker接口而已,而Invoker嵌套Invoker就是一个不错的设计,改造完成后:
FailfastClusterInvoker.invoke--->protocolInvoker.invoke-->网络---->服务端
invoker.invoke--->ref 服务
依次类推, dubbo 内部有非常多的 invoker 包装类, 它们层层嵌套, 但 rpc
流程不关心细节, 只傻瓜式地调用其 invoke 方法, 剩下的逻辑自会传递到
最后一个 invoker 进行网络调用。
3. 针对Dubbo系统默认的策略,我发现是的 容错 Invoker ------> 嵌套 负载均衡Invoker -----> 嵌套 网络协议Invoker ----> 过滤器Invoker ----> 最原始的 Invoker。
4. 其实,容错、负载均衡、过滤器都可以单独使用。
而如何搭配使用的话,容错invoker 内部嵌套 负载均衡的invoker;
没有负载均衡的话,那就是 容错invoker 内部嵌套 过滤器的invoker;
全部使用的话 容错invoker 内部嵌套 负载均衡的invoker 内部再嵌套 过滤器的invoker;
5. 过滤器是一个链式调用关系,会逐个调用
其实,这只是目前发现的一种微妙关系,并不代表就一定是这样的。如有错误,请多指正
Dubbo这种玩意,真正的威力是扩展功能。 而扩展是基于Dubbo SPI思想得以实现的。如果有人问我阅读Dubbo源码最大的收益是什么,我的回答一定是Dubbo SPI设计思想。