前期準備
<dubbo:service/> 服務配置,用于暴露一個服務,定義服務的元信息,一個服務可以用多個協議暴露,一個服務也可以注冊到多個注冊中心。
eg、<dubbo:service ref="demoService" interface="com.xxx.xxx.provider.DemoService" />
<dubbo:reference/> 引用服務配置,用于創建一個遠程服務代理,一個引用可以指向多個注冊中心。
eg、<dubbo:reference id="demoService" interface="com.xxx.xxx.provider.DemoService" />
<dubbo:protocol/> 協議配置,用于配置提供服務的協議信息,協議由提供方指定,消費方被動接受。
eg、<dubbo:protocol name="dubbo" port="20880" />
<dubbo:application/> 應用配置,用于配置當前應用信息,不管該應用是提供者還是消費者。
eg、<dubbo:application name="provider" />
<dubbo:module/> 模塊配置,用于配置當前模塊信息,可選。
<dubbo:registry/> 注冊中心配置,用于配置連接注冊中心相關信息。
eg、<dubbo:registry address="zookeeper://192.168.2.249:2181" />
<dubbo:monitor/> 監控中心配置,用于配置連接監控中心相關信息,可選。
<dubbo:provider/> 提供方的缺省值,當ProtocolConfig和ServiceConfig某屬性沒有配置時,采用此缺省值,可選。
<dubbo:consumer/> 消費方缺省配置,當ReferenceConfig某屬性沒有配置時,采用此缺省值,可選。
<dubbo:method/> 方法配置,用于ServiceConfig和ReferenceConfig指定方法級的配置信息。
<dubbo:argument/> 用于指定方法參數配置。
Invoker URL ServiceBean
URL 之于 Dubbo,猶如水之于魚,非常重要。
在 Dubbo 中,Invoker 是一個非常重要的模型。在服務提供端,以及服務引用端均會出現 Invoker。Dubbo 官方文檔中對 Invoker 進行了說明,這里引用一下。
Invoker 是實體域,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉換成它,它代表一個可執行體,可向它發起 invoke 調用,它有可能是一個本地的實現,也可能是一個遠程的實現,也可能一個集群實現。
1.概覽
http://dubbo.apache.org/zh-cn/docs/dev/implementation.html
<dubbo:service/> → ServiceConfig→Invoker→ Exporter->啟動server打開端口->注冊
1.dubbo在什么時候進行服務暴露
[圖片上傳失敗...(image-aaadb7-1562576327193)]
[圖片上傳失敗...(image-ff9460-1562576327192)]
導出時機:
afterPropertiesSet
onApplicationEvent
為什么要延遲暴露
dubbo服務導出到那里?
導出了什么東西
怎么導出的
2.服務暴露過程
- 1.檢查準備環境配置
- 2.加載注冊中心
- 3.暴露本地服務
- 4.暴露遠程服務
- 5.注冊
導出的入口
org.apache.dubbo.config.ServiceConfig#export
- 檢測 <dubbo:service> 標簽的 interface 屬性合法性,不合法則拋出異常
- 檢測 ProviderConfig、ApplicationConfig 等核心配置類對象是否為空,若為空,則嘗試從其他配置類對象中獲取相應的實例。
- 檢測并處理泛化服務和普通服務類(擴展泛化調用)
-
檢測本地存根配置,并進行相應的處理(解釋存根)image
- 對 ApplicationConfig、RegistryConfig 等配置類進行檢測,為空則嘗試創建,若無法創建則拋出異常
檢查配置舉個例子 protocol
如果為空就set,然后增加屬性
[圖片上傳失敗...(image-819df9-1562576327192)]
- 2.加載注冊中心
[圖片上傳失敗...(image-10b9e0-1562576327192)]
[圖片上傳失敗...(image-3c064c-1562576327192)]
org.apache.dubbo.config.AbstractInterfaceConfig#loadRegistries的registries什么時候注入的?提出問題
org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, boolean, boolean)
這里獲取默認的配置 擴展IOC
雙注冊中心
haunt 資料
https://tech.youzan.com/haunt-youzan-service-discovery/
org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
1.將一些信息,比如版本、時間戳、方法名以及各種配置對象的字段信息放入到 map 中,map 中的內容將作為 URL 的查詢字符串。構建好 map 后,緊接著是獲取上下文路徑、主機名以及端口號等信息。最后將 map 和主機名等數據傳給 URL 構造方法創建 URL 對象。
[圖片上傳失敗...(image-70bad3-1562576327192)]
3.暴露本地服務org.apache.dubbo.config.ServiceConfig#exportLocal
先看一下成員變量。使用spi
[圖片上傳失敗...(image-53fddf-1562576327192)]
[圖片上傳失敗...(image-d81aee-1562576327192)]
創建invoker的過程
[圖片上傳失敗...(image-47e326-1562576327192)]
生成的protocol code
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo": url.getProtocol());
if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo": url.getProtocol());
if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
String extName = (url.getProtocol() == null ? "dubbo": url.getProtocol());
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
最后得到
org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
[圖片上傳失敗...(image-d3033f-1562576327192)]
回憶getExtension的時候,對Protocol包裝又包裝。
build一個過濾鏈,啟動一個服務質量監控服務器,增加一個監聽者。
[圖片上傳失敗...(image-93aad6-1562576327192)]
為什么要有本地暴露
本地調用使用了 injvm 協議,是一個偽協議,它不開啟端口,不發起遠程調用,只在 JVM 內直接關聯,但執行 Dubbo 的 Filter 鏈。
http://dubbo.apache.org/zh-cn/docs/user/demos/local-call.html
org.apache.dubbo.registry.integration.RegistryProtocol#doLocalExport
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
[圖片上傳失敗...(image-ffbf2d-1562576327192)]
[圖片上傳失敗...(image-1fea4f-1562576327192)]
org.apache.dubbo.rpc.Protocol#export 遠程暴露
org.apache.dubbo.registry.integration.RegistryProtocol#export
遠程暴露第一步 交給具體的協議去暴露本地端口
org.apache.dubbo.registry.integration.RegistryProtocol#doLocalExport
→org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#export(緩存exporter)
→org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer(緩存server)
→org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#createServer(給url增加一些屬性)
->org.apache.dubbo.remoting.exchange.Exchangers#bind(org.apache.dubbo.common.URL, org.apache.dubbo.remoting.exchange.ExchangeHandler)(根據type獲取對應的exchanger)
->org.apache.dubbo.remoting.Transporters#bind(org.apache.dubbo.common.URL, org.apache.dubbo.remoting.ChannelHandler...)(門面模式,具體交給netty去做)
->org.apache.dubbo.remoting.transport.netty4.NettyServer#doOpen(真正啟動server的地方)
[圖片上傳失敗...(image-498383-1562576327192)]
第二步-注冊
獲取注冊中心
org.apache.dubbo.registry.support.AbstractRegistryFactory#getRegistry
→org.apache.dubbo.registry.etcd.EtcdRegistryFactory#createRegistry
→org.apache.dubbo.remoting.etcd.jetcd.JEtcdTransporter#connect
[圖片上傳失敗...(image-c652f9-1562576327192)]
org.apache.dubbo.remoting.etcd.support.AbstractEtcdClient#create
[圖片上傳失敗...(image-3893ab-1562576327192)]
org.apache.dubbo.remoting.etcd.jetcd.JEtcdClientWrapper#createPersistent
[圖片上傳失敗...(image-71c6f8-1562576327192)]
模板方法
重點是把dubbo的url轉換成etcd的節點
為啥注冊中心掛了,服務還能繼續通信?
com.alibaba.dubbo.registry.RegistryService#subscribe
最后。官網的說明
URL舉例
服務提供者暴露一個服務的詳細過程
上圖是服務提供者暴露服務的主過程:
首先 ServiceConfig
類拿到對外提供服務的實際類 ref(如:HelloWorldImpl),然后通過 ProxyFactory
類的 getInvoker
方法使用 ref 生成一個 AbstractProxyInvoker
實例,到這一步就完成具體服務到 Invoker
的轉化。接下來就是 Invoker
轉換到 Exporter
的過程。
Dubbo 處理服務暴露的關鍵就在 Invoker
轉換到 Exporter
的過程,上圖中的紅色部分。下面我們以 Dubbo 和 RMI 這兩種典型協議的實現來進行說明:
Dubbo 的實現
Dubbo 協議的 Invoker
轉為 Exporter
發生在 DubboProtocol
類的 export
方法,它主要是打開 socket 偵聽服務,并接收客戶端發來的各種請求,通訊細節由 Dubbo 自己實現。