前面我們做了一大堆的準備工作,包括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類和,自己去對應的包下新建目錄,把控制檯的代碼拷貝過去
最後將日誌級別改爲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的變化