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