美文网首页dubbo源码
dubbo client引用远程服务逻辑探究

dubbo client引用远程服务逻辑探究

作者: Foghost | 来源:发表于2023-01-03 11:39 被阅读0次

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

    相关文章

      网友评论

        本文标题:dubbo client引用远程服务逻辑探究

        本文链接:https://www.haomeiwen.com/subject/vyhlcdtx.html