Dubbo源码解析 — 服务的创建和暴露
前言
本文主要就dubbo基础的XML解析开始,到服务的创建和发布,最终延展到底层netty的调用,给出一个初始化流程的解读。
在这里你能看到dubbo一些优秀的抽象设计,以及底层源代码的解读。如有语义不明,联系紫苑。或者邮件ziyuan@2dfire.com。
service和server
首先,当发布一个服务的Server时,我们需要编写一个Spring配置文件,形如以下:
1 | <beans xmlns="..."> |
其中关键的一行为
而我们观察这个NamespaceHandler的代码,非常的简短,这里就不贴了。实际就重写了NamespaceHandlerSupport的init方法,注册了一些BeanDefinitionParser,而读过spring源码的人都应该了解,spring的启动流程:先根据xml生成beanDefinition,然后再根据beanDefinition生成Bean并放到一个concurentHashMap中。那么我们直接看DubboNamespaceHandler中注册的DubboBeanDefinitionParser,这里有这么一行代码:
1 | registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); |
DubboBeanDefinitionParser中主要做了一些BeanDefinition的初始化,我们跳过直接看ServiceBean。
同样我们省略一些初始化(这里其实是初始化了一些config,比如protocolConfig、monitorConfig等等,具体细节查看源码),在初始化的最后我们找到了这么几行代码:
1 | /* 省略一些初始化操作 */ |
export即为我们的发布操作,二话不说点进去。
export是ServiceConfig中的方法,而ServiceBean继承自ServiceConfig(这个地方的抽象是不是有点不太合适?思考中…),在export中我们看到了这么几个现象:
- delay的使用:
1 | if (delay != null && delay > 0) { |
我们发现,delay的作用就是延迟暴露,而延迟的方式也很直截了当,Thread.sleep(delay),另外一个比较有意思的就是我们可以看到,延迟暴露的服务是通过守护线程发布的,而守护线程的优先级比较低,这里的设计是不是有何用意,有兴趣的同学可以细究一下或者有知道的告诉我一下。
- export是synchronized修饰的方法。也就是说暴露的过程是原子操作,正常情况下不会出现锁竞争的问题,毕竟初始化过程大多数情况下都是单一线程操作,这里联想到了spring的初始化流程,也进行了加锁操作,这里也给我们平时设计一个不错的启示:初始化流程的性能调优优先级应该放的比较低,但是安全的优先级应该放的比较高!
继续看doExport()方法。同样是一堆初始化代码,这里我们看到了如果provider已经存在(provider != null)的情况下,我们之前说过的初始化的monitorConfig、moudleConfig中的一些配置是不会生效的(protocol、monitor等属性),而是直接从已有的provider中获取的,而provider的初始化是在serviceBean中,根据:
1 | //如果上下文已经存在的情况下,从上下文中获取providers |
获得。
export过程
继续看doExport(),最终会调用到doExportUrls()中:
1 | //DUBBO框架整体是基于URL为总线的方式来达到各个模块之间的动态加载,这里也有体现,后面我们在dubbo的插件化中会特别讲解 |
继续看doExportUrlsFor1Protocol()方法之前,我们会发现,实际上发布服务的是protocol,这里的抽象做的也很精彩,毕竟各个protocol的内容大不相同,protocol后面还会讲解,默认的protocol是dubboProtol。在doExportUrlsFor1Protocol中,我们会看到如下语句:
1 | ... |
这里我们会发现:
- dubbo的比较核心的抽象之一:Invoker。
- invoke实际上是一个代理类,从proxyFactory(默认为javassist的proxyFactory)中生成。
这里我们也做一个小总结,各个dubbo组件的作用:
- Invoker – 执行具体的远程调用
- Protocol – 服务地址的发布和订阅
- Exporter – 暴露服务或取消暴露
protocol发布服务
我们看一下dubboProtocol的export方法:
1 | ... |
省略一些步骤和判断,直接看openServer:
1 | ... |
继续看其中的createServer方法:
1 | //requestHandler包含通信协议的主要处理逻辑 |
发现ExchangeServer是通过Exchangers创建的,直接看bind方法:
1 | ... |
getExchanger方法实际上调用的是ExtensionLoader的相关方法,这里的ExtensionLoader是dubbo插件化的核心,我们会在后面的插件化讲解中详细讲解,这里我们只需要知道Exchanger的默认实现只有一个:HeaderExchanger。上面一段代码最终调用的是:
1 | public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { |
可以看到Server与Client实例均是在这里创建的,HeaderExchangeServer需要一个Server类型的参数,来自Transporters.bind():
1 | public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException { |
getTransporter()获取的实例来源于配置,默认返回一个NettyTransporter:
1 | public Server bind(URL url, ChannelHandler listener) throws RemotingException { |
最终来到了NettyServer,在它的doOpen()方法中看到了我们熟悉的Netty Bootstrap~~,至此,我们一个provider的注册完成,可以看到底层dubbo的默认实现时netty。