美文网首页dubbo源码
dubbo server服务导出(暴露)过程探究

dubbo server服务导出(暴露)过程探究

作者: Foghost | 来源:发表于2022-12-29 18:07 被阅读0次

    本文主要聊下dubbo的服务导出部分,即服务暴露,服务导出的核心接口是 Protocol 的 export方法;暴露的方式可以有很多种(tcp/http/rmi/webservice等),也可以同时暴露多种方式,dubbo 基于接口 SPI 的扩展非常灵活,完全看协议本身实现;

    服务端接口声明及实现如下:

    public interface DemoService {
        String sayHello(String name);
    }
    public class DemoServiceImpl implements DemoService {
        @Override
        public String sayHello(String name) {
            return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
        }
    }
    public class Provider {
        private static void main(String[] args) {
            DemoService demoService = new DemoServiceImpl();
            ApplicationConfig application = new ApplicationConfig();
            application.setName("demo");
            RegistryConfig registry = new RegistryConfig();
            registry.setAddress("127.0.0.1:2181");
            registry.setProtocol("zookeeper");
            ProtocolConfig protocol = new ProtocolConfig();
            protocol.setName("dubbo");
            protocol.setPort(20880);
            protocol.setServer("netty4");
            ServiceConfig<DemoService> service = new ServiceConfig<DemoService>(); 
            service.setApplication(application);
            service.setRegistry(registry); 
            service.setProtocol(protocol); 
            service.setInterface(DemoService.class);
            service.setRef(demoService);
            service.export();
            System.in.read();
        }
    }
    

    由于 dubbo 最近在做比较大的版本变迁,但是其核心接口并没变,所以这里以 2.6.x的版本为例讲解,由于我们选用的 server 实现是netty4,所以我们在 netty 的 AbstractBootstrap.bind 方法加入一个断点,得到的调用栈如下:

    25. bind:264, AbstractBootstrap (io.netty.bootstrap)
    24. doOpen:119, NettyServer (com.alibaba.dubbo.remoting.transport.netty4)
    23. <init>:88, AbstractServer (com.alibaba.dubbo.remoting.transport)
    22. <init>:81, NettyServer (com.alibaba.dubbo.remoting.transport.netty4)
    21. bind:33, NettyTransporter (com.alibaba.dubbo.remoting.transport.netty4)
    20. bind:-1, Transporter$Adaptive (com.alibaba.dubbo.remoting)
    19. bind:60, Transporters (com.alibaba.dubbo.remoting)
    18. bind:46, HeaderExchanger (com.alibaba.dubbo.remoting.exchange.support.header)
    17. bind:72, Exchangers (com.alibaba.dubbo.remoting.exchange)
    16. createServer:430, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)
    15. openServer:393, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)
    14. export:371, DubboProtocol (com.alibaba.dubbo.rpc.protocol.dubbo)
    13. export:123, ProtocolFilterWrapper (com.alibaba.dubbo.rpc.protocol)
    12. export:59, ProtocolListenerWrapper (com.alibaba.dubbo.rpc.protocol)
    11. export:-1, Protocol$Adaptive (com.alibaba.dubbo.rpc)
    10. doLocalExport:172, RegistryProtocol (com.alibaba.dubbo.registry.integration)
        9.4 - doRegister:140, ZookeeperRegistry (com.alibaba.dubbo.registry.zookeeper)
        9.3 - register:150, FailbackRegistry (com.alibaba.dubbo.registry.support)
        9.2 - register:129, RegistryProtocol (com.alibaba.dubbo.registry.integration)
    9. export:135, RegistryProtocol (com.alibaba.dubbo.registry.integration)
    8. export:120, ProtocolFilterWrapper (com.alibaba.dubbo.rpc.protocol)
    7. export:56, ProtocolListenerWrapper (com.alibaba.dubbo.rpc.protocol)
    6. export:-1, Protocol$Adaptive (com.alibaba.dubbo.rpc)
    5. doExportUrlsFor1Protocol:514, ServiceConfig (com.alibaba.dubbo.config)
    4. doExportUrls:359, ServiceConfig (com.alibaba.dubbo.config)
    3. doExport:318, ServiceConfig (com.alibaba.dubbo.config)
    2. export:216, ServiceConfig (com.alibaba.dubbo.config)
    1. main:, Provider (com.alibaba.dubbo.demo.provider)
    
    

    服务暴露主要分两个步骤,一个是本地服务暴露,一个是服务注册,上面的调用栈主要体现的是本地暴露,支线 9.2 是服务注册逻辑,下面从栈底依次向上进行拆解:
    1、 入口函数main函数

    2-3、 ServiceConfig 是服务配置类,承担配置的解析、校验、组装及服务暴露的调用入口,防止重复导出等

    4、 ServiceConfig.doExportUrls 如果是多协议则循环导出

    5、 doExportUrlsFor1Protocol 顾名思义,是对一个协议进行暴露导出,这里有很多的代码是对 URL 及其参数部分进了组装,然后还会进行 injvm 的导出,然后进行 ProtocolProtocol 导出,这里看起来可能会有些疑惑,为什么都是用 protocol.export 进行导出的,但是导出的结果却不一样,这里就是 SPI 自适应的能力了,具体来书就是 protocol.export 会根据传入参数的 url 进行查找合适的实现类进行 export;本地导出的时候将 url的protocol设置为 injvm,注册中心导出的时候将protocol设置为 registry就会分别找到各自的实现类进行导出,这里牵涉到 SPI 的自适应 @Adaptive 注解的实现,就不展开讲了;

    6、 Protocol$Adaptive 就是 SPI 自适应的实现,它是采用代码生成的方式实现的,所以这里看不到源码

    7、 ProtocolListenerWrapper 是 Protocol 的 SPI Wrapper 增强类,他会被自动装饰在 Protocol 实现类上,这还是 SPI 的功能;在这里因为是注册中心导出逻辑会直接跳过过滤器链的组装逻辑,可以查看 ProtocolListenerWrapper.export 方法

    8、 ProtocolFilterWrapper 也是 Protocol 的 SPI Wrapper 增强类,他的主要作用是创建过滤器链,如果你看过客户端调用的那篇文章,这里类也出现过,总的来说就是为服务端及客户端构建过滤器链;在这里因为是注册中心导出逻辑会直接跳过过滤器链的组装逻辑,详情可查看 ProtocolFilterWrapper.export 方法

    9、 RegistryProtocol 是注册中心导出核心类, 内部封装了注册服务的通用逻辑,内部通过相应的 registry 接口适配不同的注册中心;9.2 - 9.4 是注册服务的逻辑;

    10、 RegistryProtocol.doLocalExport 这个方法有点眼熟,在 ServiceConfig 中也有一个 exportLocal 的方法是用来导出 injvm Protocol的,但是 RegistryProtocol.doLocalExport 的作用是用来导出真实的应用协议的在这里即是 DubboProtocol

    11-13、 又是熟悉的一套 SPI 自适应导出协议,这里url的protocol已经是 dubbo 了,ProtocolListenerWrapper和ProtocolFilterWrapper分别进行增强加入监听和过滤器链,这里会进行构造过滤器链的逻辑

    14、 进行 DubboProtocol 的导出逻辑,将 Invoker 及过滤器组成的链表头包装成一个 exporter 进行缓存,等真实业务网络请求到来时,可以在缓存中找到真实的 Invoker 链进行调用

    15、 DubboProtocol.openServer 开始暴露网络接口,即网络监听,由于端口只能监听一次,如果有多个接口暴露在一个端口上这里会进行端口复用,即监听完成后缓存起来,下次判断 host:port 是否已缓存监听,如果已监听则复用

    16、 DubboProtocol.createServer 创建server监听逻辑,最终调用 Exchangers.bind 开启一个网络端口;值得一提的是 DubboProtocol 中创建了一个 ExchangeHandlerAdapter 类型的 requestHandler 来进行网络请求处理,他的主要作用是将网络请求参数到 exporter 缓存中找到对应的 Invoker 进行调用然后返回;

    17-18、 Exchangers 是 Exchanger 的工厂来类,通过 SPI 找到 Exchanger 的实现类 HeaderExchanger 进行具体实现

    19-21、 Transporters 是 Transporter 的工厂类,通过 SPI 找到 Transporter 的实现类 NettyTransporter 进行端口监听,细心的同学可能发现了 这里有一个 Transporter$Adaptive.bind 而 Exchanger 没有,这是因为他们获取实现类时的调用方式不太一样,个人认为更应该使用 Adaptive 类进行操作,也可能是 Exchanger 的用处比较少,扩展的可能性不大吧,其实现类默认也只有一个,而 Transporter 的实现类就多了,扩展的可能性也非常大

    22-25、 调用最终的 nettyServer 实现进行端口监听;

    相关文章

      网友评论

        本文标题:dubbo server服务导出(暴露)过程探究

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