本文主要记录下自己阅读源码时的一些理解,本文主要聊下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 进行实现类适配
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 优秀的抽象化,模块化,层次化设计,代码的复用性很高,直连的实现逻辑也很简单


Foghost
58 声望0 粉丝