本文主要聊下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 实现进行端口监听;


Foghost
58 声望0 粉丝