前面我們做了一大堆的準備作業,包括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的變化

--------------以上皆原創,給未來的自己留下一點學習的痕跡!--------
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/386456.html
標籤:Java
