dubbo源码分析6(服务暴露之本地暴露)
前面我们做了一大堆的准备工作,包括dubbo是怎么跟spring进行整合的,然后一步一步是怎么找到启动入口的,而且还知道了,由于我们的dubbo的版本是2.7.5,所以其实启动的入口是DubboBootstrap类,一切的开始都从这个启动器开始出发
下面,开始我们这一次的开车之旅;
提前须知:
服务提供者肯定就是提供一个接口,然后供别的消费者使用的,那么问题来了,服务提供者怎么做才能提供给别的人使用呢?
其实服务提供者在初始化的时候,做了6件事(假设注册中心是zookeeper)
(1) 启动容器(就是spring的ioc容器)
(2)暴露服务(本地暴露和远程暴露)
(3)开启netty服务端
(4)连接zookeeper
(5)将服务信息写入zookeeper
(6)监听zookeeper节点(或者说订阅服务信息)
只要做完了这6件事情,一个新鲜可口的服务就可以使用了,我们可以使用官方的图来看看,下图所示,其实就是完成了下图中的步骤0和步骤1
1.启动容器
大家猜猜我们启动一个dubbo服务需要些什么东西?需要tomcat吗?
其实启动dubbo服务的话,我们只需要有spring+注册中心就可以了,其他的都不需要,为什么不需要tomcat这种servlet容器啊?
你想想啊,我们在服务提供者这里,使用到了Controller了么?使用到了http协议了么?都没有吧,而且如果dubbo需要使用servlet容器,那么还需要额外消耗内存,性能也会下降
dubbo启动方式有三种
1.1 servlet容器:这种是最消耗性能的,我们肯定不会使用
1.2 自己写一个main方法运行(Spring容器):官方demo使用的就是这种方式,下图所示,其实就是我们自己创建一个spring容器,去启动,就能加载自定义的配置文件,这种方式适用于测试和调试(我们看源码就是使用这种启动方式)
1.3 官方提供的启动方式:org.apache.dubbo.container.Main类有个main方法,这里可以启动,而且可以看到默认使用的是SpringContainer
我们看看SpringContainer类中的start()方法,发现也是sping容器启动的
如果到了这里你还是不会启动,官方还提供了脚本方式启动,脚本给你准备好了,可以支持你的服务在linux中去启动
总结一下,有三种启动dubbo服务的方式,第一种是使用tomcat等servlet容器去启动,第二种是我们自己简单的使用一下spring容器加载配置文件,第三种就是官方提供的Main类,内部对spring容器的启动和停止进行了更好的封装,并提供了一系列的服务端启动脚本
开发调试的时候可以使用第二种,生产上线使用第三种,第一种狗都不用( ̄▽ ̄)ノ,哈哈哈哈
2.暴露服务(本地暴露和远程暴露)
记得前面说过,暴露服务分为本地暴露和远程暴露,再说明一下,为什么会有本地暴露?
就是当前同一个jvm中有其他的服务要使用到当前的服务,肯定不会去走注册中心调用服务呀(就类似于你家开餐馆的,难道你吃东西会使用美团外卖点自己家的菜么?生草๑乛◡乛๑)
我们还是从DubboBootstrap的start()方法开始看:
看看下面SerrviceBean的类继承图,这个export()方法在ServiceConfig类中
继续跟进这个doExport()方法
上面说了一大堆的垃圾话,接下来才是重点,由于下面这个方法太长,我把代码拷贝下来,然后大篇幅的省略一些代码
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { //省略了好多好多代码 String scope = url.getParameter(SCOPE_KEY); if (!SCOPE_NONE.equalsIgnoreCase(scope)) { //1.本地暴露服务 if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) { exportLocal(url); } //2.远程暴露服务 if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) { //2.1如果存在注册中心,就把服务暴露到注册中心 if (CollectionUtils.isNotEmpty(registryURLs)) { for (URL registryURL : registryURLs) { //省略代码 url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY)); //2.1.1 加载dubbo的监控中心,如果有监控中心 //就将配置添加到url中 URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL); if (monitorUrl != null) { url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString()); } //省略一些代码 String proxy = url.getParameter(PROXY_KEY); if (StringUtils.isNotEmpty(proxy)) { registryURL = registryURL.addParameter(PROXY_KEY, proxy); } //2.1.2 这里将需要导出的服务封装成Invoker对象 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //2.1.3 将invoker导出到注册中心,并生成exporter Exporter<?> exporter = protocol.export(wrapperInvoker); //2.1.4 将export收集 exporters.add(exporter); } //2.2 没有服务注册中心,就采用直连的方式导出服务,不需要配置监听,直接将invoker暴露到注册中心,并生成exporter } else { Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter); } //省略代码 } } this.urls.add(url); }
接下来又是一个很重要的地方!!!这个invoker表示一个什么呀?
我们首先需要将日志级别修改一下:
然后重新启动调试,根据控制台打印的ProxyFactory$Adaptive类和Protocol$Adaptive类,自己去对应的包下新建目录,把控制台的代码拷贝过去
最后将日志级别改为info,重新启动调试就好了,嘿嘿!
没办法,就是要这样奇葩的操作,才能在等会儿调试的时候才会进入到这两个类里面(-_-メ)
3.本地暴露服务
进入到ServiceConfig的doExportUrlsFor1Protocol()方法,然后下图的exportLocal(url)方法
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { //省略了好多好多代码 String scope = url.getParameter(SCOPE_KEY); if (!SCOPE_NONE.equalsIgnoreCase(scope)) { //1.本地暴露服务 if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) { exportLocal(url); }
注意下图中URL local,是以injvm开头的,说明是暴露在本地jvm中的(后续会说到的,如果是暴露在远程的,那就是registry开头的)
在上图的getInvoker方法,就是进入到之前我们自己创建的那个类中,下面就贴一下好多代码当流程图看了(-_-メ)
最后就是进入到InjvmExporter的构造器了
注意:这里的 exporterMap对这个exporter做了缓存, 后面这个缓存map会很有用的
4.总结
根据服务暴露到jvm的流程,总结出来就是下面这个图,应该很清晰了,就是将当前服务提供者中的服务首先使用代理工厂封装成Invoker,然后调用Protocal的export()方法,将invoker转换为Exporter,并且还会缓存一份这个Exporter在exporterMap中;
后面暴露远程服务会稍微比这个复杂一丢丢,就是增加了本地启动netty的步骤和远程操作zookeeper创建节点以及监听zookeeper的变化