之前 分析 consumer代理建立和發送請求的時候,進行了簡單的分層,實際上并不準确, 本文将根據 dubbo開發文檔中的分層進行分析,下面列出
整體設計
圖例說明:
圖中左邊淡藍背景的為服務消費方使用的接口,右邊淡綠色背景的為服務提供方使用的接口,位于中軸線上的為雙方都用到的接口。
圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關系,每一層都可以剝離上層被複用,其中,Service 和 Config 層為 API,其它各層均為 SPI。
圖中綠色小塊的為擴充接口,藍色小塊為實作類,圖中隻顯示用于關聯各層的實作類。
圖中藍色虛線為初始化過程,即啟動時組裝鍊,紅色實線為方法調用過程,即運作時調時鍊,紫色三角箭頭為繼承,可以把子類看作父類的同一個節點,線上的文字為調用的方法。
各層說明
config 配置層:對外配置接口,以 ServiceConfig, ReferenceConfig 為中心,可以直接初始化配置類,也可以通過 spring 解析配置生成配置類
proxy 服務代理層:服務接口透明代理,生成服務的用戶端 Stub 和伺服器端 Skeleton, 以 ServiceProxy 為中心,擴充接口為 ProxyFactory
registry 注冊中心層:封裝服務位址的注冊與發現,以服務 URL 為中心,擴充接口為 RegistryFactory, Registry, RegistryService
cluster 路由層:封裝多個提供者的路由及負載均衡,并橋接注冊中心,以 Invoker 為中心,擴充接口為 Cluster, Directory, Router, LoadBalance
monitor 監控層:RPC 調用次數和調用時間監控,以 Statistics 為中心,擴充接口為 MonitorFactory, Monitor, MonitorService
protocol 遠端調用層:封将 RPC 調用,以 Invocation, Result 為中心,擴充接口為 Protocol, Invoker, Exporter
exchange 資訊交換層:封裝請求響應模式,同步轉異步,以 Request, Response 為中心,擴充接口為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport 網絡傳輸層:抽象 mina 和 netty 為統一接口,以 Message 為中心,擴充接口為 Channel, Transporter, Client, Server, Codec
serialize 資料序列化層:可複用的一些工具,擴充接口為 Serialization, ObjectInput, ObjectOutput, ThreadPool
代理建立
下面是 serve代理建立-訂閱方式的前半部分
ServiceConfig<T>.export()//轉發
ServiceConfig<T>.doExport() //讀取配置并檢查
ServiceConfig<T>.doExportUrls() //擷取一個或多個注冊中心的配置,如果有多個協定(通常隻有一個dubbo),每個協定執行一次 export
ServiceConfig<T>.doExportUrlsFor1Protocol(ProtocolConfig, List<URL>) 建立目标接口 動态代理invoker, 緩存服務對象exporter
詳細步驟如下
- 擷取 application,module,privoder,protocol 配置
- 擷取method配置
- get /set 各種參數
- 沒有配置遠端注冊位址,執行本地export, 否則執行注冊。
config 配置層:讀取配置,解析spring配置,初始化配置,
細節:多種協定執行暴露服務,建立目标接口 動态代理invoker, 緩存服務對象exporter
proxy 服務代理層:建立目标接口 動态代理invoker,
cluster 路由層;server端沒有用到
ProtocolListenerWrapper.export(Invoker<T>)//轉發執行 export
ProtocolFilterWrapper.export(Invoker<T>)//轉發執行 export
QosProtocolWrapper.export(Invoker<T>)//啟動 qos服務 轉發執行 export
QosServer服務于telnet通路和 http通路。
這部分隻是執行了轉發,推測是由于 協定嵌套的原因導緻的
RegistryProtocol.export(Invoker<T>) //暴露服務,注冊,緩存服務對象exporter
RegistryProtocol.doLocalExport(Invoker<T>) //封裝了 ListenerExporterWrapper 和接口代理執行代理invoker ,作用未知
registry注冊中心層:conusmer端 擷取 providers,routes,configs配置 執行notify流程,server端 建立并緩存接口代理,啟動服務,等待接收請求,注冊provider url到zookeeper。
ProtocolListenerWrapper.export(Invoker<T>)// 封裝監聽器
ProtocolFilterWrapper.export(Invoker<T>)//建構調用鍊
QosProtocolWrapper.export(Invoker<T>)//轉發執行 export
下面是之前總結過的
過濾鍊層:ProtocolFilterWrapper 建構發送請求時要執行的過濾鍊
工具層:QosProtocolWrapper 支援 telnet,http 服務
監聽器層:ProtocolListenerWrapper 擷取invoker,執行自定義的監聽器
可以 概括為 工具鍊層: 建構發送請求時要執行的過濾鍊,支援 telnet,http 服務, 擷取invoker,執行自定義的監聽器。
下圖是server建立代理的後半部分
DubboProtocol.export(Invoker<T> 緩存exporter,啟動server key 是接口名+端口号 如tuling.dubbo.server.UserService:20880 DubboExporter 建立沒啥邏輯, 儲存了 invoker 類型是RegistryProtocol$InvokerDelegete
DubboProtocol.openServer(URL) //建立 server,緩存 server到 serverMap中
DubboProtocol.createServer(URL) //添加 心跳 時間間隔 預設60000ms
重點代碼分析
DubboProtocol.export(Invoker<T>)
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);例:
//key 規則 分組名/接口名:端口号 如 langfang/tuling.dubbo.server.UserService:20880
// key: tuling.dubbo.server.UserService:20880 沒有分組名
// 建立DubboExporter,DubboExporter 用來包裝 接口執行引用。
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);//緩存代理DubboExporter,用于 server接受資料時查找和執行。
//export an stub service for dispaching event
Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
//回調有關 不知何用
if (isStubSupportEvent && !isCallbackservice) {
String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
if (logger.isWarnEnabled()) {
logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
"], has set stubproxy support event ,but no stub methods founded."));
}
} else {
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
//上面的流程是建立了方法執行代理
openServer(url);//這裡 開始啟動服務端,組織多層handler的嵌套。
return exporter;
}
邏輯總結:
建立了方法執行代理invoker,使用接口名+端口号緩存,開始啟動服務端,組織多層handler的嵌套。
DubboProtocol.createServer(URL)
private ExchangeServer createServer(URL url) {
//預設開啟server關閉時發送readonly事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
//預設開啟heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
//...
try {
server = Exchangers.bind(url, requestHandler);//執行綁定 建立server
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
//...
}
return server;
}
這層 稱為 protocol 遠端調用層,還有其他的實作 如HessianProtocol,InjvmProtocol
server遠端調用層總結:建立了相應的exporter 并緩存,執行服務啟動流程,添加 心跳 時間間隔。
結合consumer引用建立流程分析
consumer引用建立:建立了相應的invoker并緩存,然後執行連接配接遠端服務,由于Hessian連接配接比較簡單 包含在建立invoker 的流程中
server端代理建立: 建立了相應的exporter 并緩存,然後執行服務啟動流程,由于Hessian啟動比較簡單 包含在建立exporter 的流程中。
DubboProtocol 的資料傳輸依賴于netty, 有一個單獨的啟動服務流程
HessianProtocol的資料傳輸依賴于http,比較簡單沒有單獨的流程
由此可知,不同的Protocol 實作 其建立exporter 的處理邏輯不同。
可以總結:protocol 遠端調用層 提供不類型協定的實作, 屏蔽了 invoker,exporter的建立細節。
Exchangers.bind(URL, ExchangeHandler) //check url,handler 添加配置codec=exchange,綁定最内層的handler.
HeaderExchanger.bind(URL, ExchangeHandler)// return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
HeaderExchangeServer嵌套如下圖
與consumer比較 沒有 嵌套 HeaderExchangeChannel
重點代碼分析
public HeaderExchangeServer(Server server) {
if (server == null) {
throw new IllegalArgumentException("server == null");
}
this.server = server;
this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);//心跳周期間隔時間 預設60000,
this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);//心跳逾時時間, 預設是heartbeat 的3倍
if (heartbeatTimeout < heartbeat * 2) {//heartbeatTimeout 不能小于heartbeat 的2倍
throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
}
startHeartbeatTimer();//啟動心跳檢測線程
}
HeaderExchangeServer的主要用途展現在構造方法,它擷取了心跳相關參數,啟動心跳線程,這個心跳任務 每隔一段時間向目前server的所有channel(就是所有用戶端)發送心跳。
HeaderExchangeHandler
有2個主要的方法
資料的發送 sent 分析過沒什麼用
資料的接受 received request 接受流程 和response接受流程 重要
HeaderExchangeHandler.received(Channel, Object)
處理 request 接受流程 和response接受流程
public void received(Channel channel, Object message) throws RemotingException {
channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
try {
// server端收到的是request
if (message instanceof Request) {
// handle request.
Request request = (Request) message;
if (request.isEvent()) {
handlerEvent(channel, request);
} else {
if (request.isTwoWay()) {
//正常同步請求 執行此處,handleRequest是重要的方法
Response response = handleRequest(exchangeChannel, request);
channel.send(response);
} else {
handler.received(exchangeChannel, request.getData());
}
}
} else if (message instanceof Response) {//處理響應 consmer端常用
handleResponse(channel, (Response) message);
} else if (message instanceof String) {
//目前不支援
} else {
handler.received(exchangeChannel, message);//正常不會執行
}
} finally {
HeaderExchangeChannel.removeChannelIfDisconnected(channel);
}
}
總結一下交換層在consumer,和server端的作用
consumer:啟動心跳,處理心跳請求,實作請求同步等待。
server:啟動心跳,處理心跳請求,擷取接口執行引用invoker,執行請求。
交換層 顧名思義 應該是進行了某種交換 在consumer端 将 netty的異步請求 交換為同步請求, 在sever端 請求資料 交換給 接口引用執行。
Transporters.bind(URL, ChannelHandler...)//check url,handlers.. 封裝 多個hanlder為ChannelHandlerDispatcher.
NettyTransporter.bind(URL, ChannelHandler)// return new NettyServer(url, listener);
NettyServer.<init>(URL, ChannelHandler) // 生成新的多層嵌套handler
NettyServer(AbstractServer).<init>(URL, ChannelHandler)//建立 ExecutorService executor
NettyServer.doOpen()//執行bind
關于這一層 dubbo開發手冊上的描述如下
transport 網絡傳輸層:抽象 mina 和 netty 為統一接口,以 Message 為中心,擴充接口為 Channel, Transporter, Client, Server, Codec
這個解釋已經比較清晰了。
進一步的解釋 就是 定義了網絡傳輸層的統一接口, 提供了 netty和mina實作。
重點代碼分析
NettyServer.<init>(URL, ChannelHandler)
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
//wrap 生成新的多層嵌套handler,類型與consumer中handler類型一緻
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
serialize 資料序列化層 将在後續的文章中分析。
server代理建立各層功能總結:
config 配置層:讀取配置,解析spring配置,初始化配置,
proxy 服務代理層:建立目标接口 動态代理invoker,
cluster 路由層:server端沒有用到
工具鍊層: 建構發送請求時要執行的過濾鍊,支援 telnet,http 服務, 擷取invoker,執行自定義的監聽器。
protocol 遠端調用層: 提供不類型協定的實作, 屏蔽了 invoker,exporter的建立細節
exchange 交換層:啟動心跳,處理心跳請求,擷取接口執行引用invoker,執行請求。
transport 網絡傳輸層:定義了網絡傳輸層的統一接口, 提供了 netty和mina實作.
最後總結:
本文分析了server代理建立過程中各層的功能,并結合consumer進行分析,這樣分析, 就能比較深入的了解server代理建立過程中代碼,并且能加深記憶。
分層設計是dubbo架構設計的核心,了解其功能,原理,對了解 多個重要流程的源碼如 consumer建立代理,conusmer發送流程 ,server建立代理,server接受流程等 有很大的幫助
dubbo源碼分析系列 寫到這 已經分析了 dubbo大部分的内容 剩下的還有 server處理請求和發送響應,參照前面 consumer的相關部分,能夠比較容易分析了。就不再寫文章分析了。