本文次要记录下本人浏览源码时的一些了解,本文次要聊下dubbo client是如何援用近程服务的,以应用zookeeper作为注册核心为例。

接口申明及客户端调用形式如下:

public interface DemoService {    String sayHello(String name);}public class Consumer2 {    public static void main(String[] args) {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});        context.start();        DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy        String hello = demoService.sayHello("world"); // call remote method        System.out.println(hello); // get result    }}

因为 dubbo 最近在做比拟大的版本变迁,然而其外围接口并没变,所以这里以 2.6.x的版本为例解说,因为咱们选用的 server 实现是netty4,所以咱们在 netty 的 NettyClient.doConnect 办法退出一个断点,失去的调用栈如下:
以dubbo官网demo为例的客户端调用栈:

34. doConnect:116, NettyClient (com.alibaba.dubbo.remoting.transport.netty4)33. connect:353, AbstractClient (com.alibaba.dubbo.remoting.transport)32. <init>:130, AbstractClient (com.alibaba.dubbo.remoting.transport)31. <init>:64, NettyClient (com.alibaba.dubbo.remoting.transport.netty4)30. connect:39, NettyTransporter (com.alibaba.dubbo.remoting.transport.netty4)29. connect:-1, Transporter$Adaptive (com.alibaba.dubbo.remoting)28. connect:81, Transporters (com.alibaba.dubbo.remoting)27. connect:40, HeaderExchanger (com.alibaba.dubbo.remoting.exchange.support.header)26. connect:111, Exchangers (com.alibaba.dubbo.remoting.exchange)25. initClient:612, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)24. getSharedClient:569, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)23. getClients:533, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)22. refer:504, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)21. refer:71, ProtocolListenerWrapper (com.alibaba.dubbo.rpc.protocol)20. refer:133, ProtocolFilterWrapper (com.alibaba.dubbo.rpc.protocol)19. refer:-1, Protocol$Adaptive (com.alibaba.dubbo.rpc)18. toInvokers:530, RegistryDirectory (com.alibaba.dubbo.registry.integration)17. refreshInvoker:349, RegistryDirectory (com.alibaba.dubbo.registry.integration)16. notify:305, RegistryDirectory (com.alibaba.dubbo.registry.integration)15. notify:490, AbstractRegistry (com.alibaba.dubbo.registry.support)14. doNotify:305, FailbackRegistry (com.alibaba.dubbo.registry.support)13. notify:290, FailbackRegistry (com.alibaba.dubbo.registry.support)12. doSubscribe:241, ZookeeperRegistry (com.alibaba.dubbo.registry.zookeeper)11. subscribe:216, FailbackRegistry (com.alibaba.dubbo.registry.support)10. subscribe:219, RegistryDirectory (com.alibaba.dubbo.registry.integration)9. doRefer:309, RegistryProtocol (com.alibaba.dubbo.registry.integration)8. refer:290, RegistryProtocol (com.alibaba.dubbo.registry.integration)7. refer:68, ProtocolListenerWrapper (com.alibaba.dubbo.rpc.protocol)6. refer:130, ProtocolFilterWrapper (com.alibaba.dubbo.rpc.protocol)5. refer:-1, Protocol$Adaptive (com.alibaba.dubbo.rpc)4. createProxy:402, ReferenceConfig (com.alibaba.dubbo.config)3. init:334, ReferenceConfig (com.alibaba.dubbo.config)2. get:163, ReferenceConfig (com.alibaba.dubbo.config)1. main:35, Consumer2 (com.alibaba.dubbo.demo.consumer)

上面进行逐渐拆解:
1、 main办法,入口
2、 ReferenceConfig.get 简略判断以后援用是否销毁,是否初始化过,否则进入初始化逻辑
3、 ReferenceConfig.init 初始化逻辑,参数校验、初始化,查看是否是泛化调用,组装URL
4、 ReferenceConfig.createProxy 查看是否是 injvm(即本地调用) 调用,解决直连url调用,否则从注册核心获取援用,包含多注册核心
5、 Protocol$Adaptive 调用Protocol的自适应实现类进行 refer,依据 url 参数中的 protocol 进行实现类适配,由 SPI 内容
6-7、 ProtocolFilterWrapper、ProtocolListenerWrapper都是 protocol 的 Wrapper 类,会主动包装在 protocol 的实现类上,由 SPI 机制实现主动注入
8、 因为目前的url中的 protocol 为 registry,所以实现类为 RegistryProtocol,该办法中将 url 的protocol 改为 zookeeper,由 SPI 获取注册核心的实现类 ZookeeperRegistry,判断有无group设置,有则应用 mergeable cluster 进行合并,没有 group 则应用 SPI 指定的 cluster 进行集群容错
9、 RegistryProtocol.doRefer 将zookeeper 目录转换为一个动静 Directory ,将客户端作为 consumer 写入到注册核心,同时订阅providers、configurators、routers,启动集群容错策略,默认是 FailoverClusterInvoker,同时依据 SPI 包装一个 MockClusterInvoker 进行本地存根、服务降级加强
10、上面开始拉取注册核心内容
11、 FailbackRegistry 是一个注册核心实现的父类,实现了注册核心容错逻辑,次要性能是如果申请注册核心失败了会主动进行重试,默认重试周期5s
12、ZookeeperRegistry.doSubscribe 进行 zookeeper 订阅申请并取得一个初始值进行 notify 回调
13、FailbackRegistry.notify 进行简略的参数校验和容错,调用 FailbackRegistry.doNotify
14、FailbackRegistry.doNotify 间接调用父类实现
15、AbstractRegistry.notify 将回调进行分类,并调用对应的回调函数
16、因为这个订阅最后是RegistryDirectory发动订阅的,回调最终执行到 RegistryDirectory.notify,这里对回调进行解决,这里解决了 configurators、routers 两种监听的回调
17、 RegistryDirectory.refreshInvoker 解决providers数据回调,刷新invoker 列表,如果没有匹配的providers则设置禁用,对所有申请返回谬误,否则将providers转换为invoker,刷新本地invoker列表,这个列表里的invoker即是具体一个ip:port的网络调用了,当然这些invoker也进行了过滤器等加强
18、RegistryDirectory.toInvokers 即是将多个 providers 的注册内容即url进行转换为具体的invoker逻辑,这里因为应用dubbo协定就是 DubboInvoker
19、初始化 invoker 逻辑,依据 SPI 协定自适应调用对应的 Protocol 实现类
20-21、 ProtocolFilterWrapper、ProtocolListenerWrapper就不多说了,协定加强类,会被 SPI 主动包装到具体的实现类上
22、因为用dubbo 这里的协定实现类是 DubboProtocol
23、getClients获取对应链接,依据配置决定是共享 client 还是独享 client
24、getSharedClient 默认是共享链接
25、initClient 因为无连贯可用,这里创立一个新的链接
26-27、Exchangers 工厂类,应用 SPI获取具体实现类 HeaderExchanger
28-30、Transporters 工厂类,依据SPI获取自适应Transporter,因为我配置了client=netty4所以这里是netty4.NettyTransporter
31-34、委托到netty4的 bootstrap.connect 具体进行链接创立

整体逻辑还是比较简单的,即从注册核心订阅到providers的url列表,转换为本地Invoker列表,并增加集群容错、过滤器等逻辑,如果用url直连的形式这个调用栈会简化很多,如下示例:

doConnect:116, NettyClient (com.alibaba.dubbo.remoting.transport.netty4)connect:353, AbstractClient (com.alibaba.dubbo.remoting.transport)<init>:130, AbstractClient (com.alibaba.dubbo.remoting.transport)<init>:64, NettyClient (com.alibaba.dubbo.remoting.transport.netty4)connect:39, NettyTransporter (com.alibaba.dubbo.remoting.transport.netty4)connect:-1, Transporter$Adaptive (com.alibaba.dubbo.remoting)connect:81, Transporters (com.alibaba.dubbo.remoting)connect:40, HeaderExchanger (com.alibaba.dubbo.remoting.exchange.support.header)connect:111, Exchangers (com.alibaba.dubbo.remoting.exchange)initClient:612, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)getSharedClient:569, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)getClients:533, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)refer:504, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)refer:73, ProtocolListenerWrapper (com.alibaba.dubbo.rpc.protocol)refer:133, ProtocolFilterWrapper (com.alibaba.dubbo.rpc.protocol)refer:-1, Protocol$Adaptive (com.alibaba.dubbo.rpc)createProxy:402, ReferenceConfig (com.alibaba.dubbo.config)init:334, ReferenceConfig (com.alibaba.dubbo.config)get:163, ReferenceConfig (com.alibaba.dubbo.config)main:35, Consumer2 (com.alibaba.dubbo.demo.consumer)

直连逻辑跳过了注册核心,实现上更间接,得益于 dubbo 优良的抽象化,模块化,层次化设计,代码的复用性很高,直连的实现逻辑也很简略