文章目錄
- 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本身是一個動态類型語言。最終調用方式跟函數調用看起來并沒有什麼差別。
而且這種動态語言的異步調用用起來也是比較舒服。