天天看點

dubbo源碼分析12-server代理建立和執行bind代理建立

之前 分析 consumer代理建立和發送請求的時候,進行了簡單的分層,實際上并不準确, 本文将根據 dubbo開發文檔中的分層進行分析,下面列出

整體設計

dubbo源碼分析12-server代理建立和執行bind代理建立

圖例說明:

圖中左邊淡藍背景的為服務消費方使用的接口,右邊淡綠色背景的為服務提供方使用的接口,位于中軸線上的為雙方都用到的接口。

圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關系,每一層都可以剝離上層被複用,其中,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代理建立-訂閱方式的前半部分

dubbo源碼分析12-server代理建立和執行bind代理建立

ServiceConfig<T>.export()//轉發

ServiceConfig<T>.doExport()    //讀取配置并檢查

ServiceConfig<T>.doExportUrls() //擷取一個或多個注冊中心的配置,如果有多個協定(通常隻有一個dubbo),每個協定執行一次 export

ServiceConfig<T>.doExportUrlsFor1Protocol(ProtocolConfig, List<URL>)     建立目标接口 動态代理invoker, 緩存服務對象exporter

詳細步驟如下

  1. 擷取 application,module,privoder,protocol 配置
  2. 擷取method配置
  3. get /set 各種參數
  4. 沒有配置遠端注冊位址,執行本地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建立代理的後半部分

dubbo源碼分析12-server代理建立和執行bind代理建立

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嵌套如下圖

dubbo源碼分析12-server代理建立和執行bind代理建立

與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的相關部分,能夠比較容易分析了。就不再寫文章分析了。

繼續閱讀