天天看點

pomelo源碼解析之子產品解析(四)pomelo-rpc

文章目錄

  • pomelo-rpc
    • pomelo-rpc/lib/rpc-server 伺服器:
    • pomelo-rpc/lib/rpc-client 用戶端
    • 總結
  • pomelo-rpc

    RPC服務是用于程序間通訊的一項技術,底層通訊基于TCP或者更上層的mqtt,ws等通信協定

    對使用者屏蔽底層細節,程序間通訊形如函數調用,用起來比較友善

    傳統開發一個程序間通訊業務,例如logonServer和gameServer之間通訊,稍微封裝下,接口大概是這樣子的:

// logonServer.h
sendMsgToGameServer(msgID, msg);
           

這樣也是屏蔽了底層通信細節,但調用了伺服器哪個函數處理的卻無法知道。

而RPC方式可以讓你像調用函數一樣進行程序間的通訊。一個好的RPC底層還會封裝好幾種協定格式。

整體來看還是比較友善的。

下邊就來具體分析下pomelo-rpc的具體實作邏輯
           
  • pomelo-rpc/lib/rpc-server 伺服器:

    RPC伺服器就是監聽一個端口,根據收到的RPC請求調用不同的子產品處理并回報

    先看一下伺服器支援的通信協定 rpc-server/acceptors

    可以發現支援mqtt mqtt2 tcp ws ws2總共5種通信協定,處理邏輯沒有差別,隻是編碼格式等有差別

    負責監聽端口,收到消息後在processMsg中處理,會調用自身的acceptor.cb去處理

    對外接口隻有一個create // pomelo-rpc/lib/server.js

    入參:

{
	paths: [
	{
		namespace: ?, // 自定義命名
		path: ?,  // 需要RPC的子產品路徑
	},
	],      
	context: ?, // 載入子產品傳入的構造參數
	port: ?, // 監聽端口
	acceptorFactory: ?, // 采用的消息協定工廠方法 預設mqtt
	reloadRemotes: ?, // 是否動态重載 會啟用檔案監聽 改變子產品後自動重載
};
           

分析server.loadRemoteServices可以發現

server會根據傳入的路徑和命名,掃描該路徑下的所有js檔案。

最終生成形如:

res = {
	namespace: {
		filename: module,
	},
 }
           

然後傳入gateway中,gateway是server的實際邏輯對象

gateway的構造函數中可以發現會建立一個接收器,并寫入一個回調函數執行dispatch.route

在dispatch.route可以看到,最終調用了儲存子產品中的具體函數進行處理

再具體子產品中處理後,可以調用回調,最終回到具體的接收器中processMsg,在這裡給用戶端做回饋

  • pomelo-rpc/lib/rpc-client 用戶端

    用戶端作為RPC的調用方主要是發送以及接收回報

    跟伺服器對應,支援5種通信協定

    mailboxes就是每種協定的具體實作,支援連接配接逾時,心跳,心跳逾時,回報逾時的處理:斷開連接配接

    在發送消息時MailBox.prototype.send會把回調函數存起來,在收到消息後找到對應的回調調用processMsg

    對外接口create入參:

{
     context: ?,  // 載入子產品傳入的構造參數
     routeContext: ?, // 自定義路由參數
     router: ?,  // 自定義路由跳轉
     routerType: ?,  // 設定路由類型
     rpcDebugLog: ?, // 是否需要日志
     mailboxFactory: ?, // 采用的消息協定工廠方法 預設mqtt
     pendingSize: ?,  // 發送緩沖大小
};
           

對于用戶端來說,也需要知道能RPC到哪裡去

添加伺服器資訊:伺服器的連接配接資訊存在client.addServer

添加對應的RPC資訊:client.addProxies

在這裡發現也需要跟伺服器讀取相同的檔案夾,或者建立一個檔案夾保持與伺服器同步(這裡隻需要函數名即可)

在client.generateProxy中可以看到:

res[name] = Proxy.create一路跟進去到proxy.genFunctionProxy可以看到,實際上并沒有載入具體的函數内容

而是根據函數名生成了一個具有兩個函數的對象proxy和toServer,而回調是綁定在了client.proxCB上

最終client形如:

client = {
    ...
      proxies: {
          namespace: {
              servertype: {
                  methodname: method,  // 這個methond不是子產品中的函數而是proxy生成的包含2個函數的對象
             };
         };
      };
  };
           

調用就是

client.proxies.namespace.servertype.methodname.proxy(toServer);

最終還是回到client.proxyCB。之是以繞了這麼一圈就是為了讓通信調用過程像函數調用一樣

toServer是指明某個server進行通信,proxy是按參數找一個通信

整體來說,采用這一套RPC做程序間全雙工通訊的話,需要每一個程序都啟動 RPC伺服器 + RPC用戶端

  • 總結

經過源碼分析我們發現RPC的整體實作邏輯還是比較簡單的。這主要得益于js本身是一個動态類型語言。最終調用方式跟函數調用看起來并沒有什麼差別。

而且這種動态語言的異步調用用起來也是比較舒服。