1. 程式人生 > >程式設計師修神之路--設計一套RPC框架並非易事

程式設計師修神之路--設計一套RPC框架並非易事


菜菜哥,我最近終於把Socket通訊調通了

這麼底層的東西你現在都會了,恭喜你離漲薪又進一步呀

http協議不也是利用的Socket嗎

可以這麼說,http協議是基於TCP協議的,底層的資料傳輸可以說是利用的socket

既然Socket通訊會了,那一個rpc的框架不就很容易就能實現了嗎?

一個比較完備的rpc框架可能並非像你所想那樣簡單,要不然人人都可以出RPC框架了

有那麼難嗎?我覺得沒有那麼難呀

如果你能解決掉這些問題,我覺得你真的是大牛了

RPC是遠端過程呼叫(Remote Procedure Call)的縮寫形式,是在多工作業系統或聯網的計算機之間執行的程式和程序所用的通訊技術

01
開局

擼碼的人都應該知道,現代程式設計中最常用的系統之間通訊方式是:http呼叫和rpc呼叫。對於同一個網路或者說是互通的網路環境中,rpc呼叫方式是系統間通訊互動最常用的方式,比基於http協議的通訊方式效能高出數倍甚至數個量級。我司的平臺rpc通訊,每秒在幾萬甚至更高,每次呼叫的通訊時間在一定程度上幾乎可以忽略不計,再加上我們首席架構師深厚的系統設計功力,採用程序內快取等等優化措施,一次rpc呼叫的整體平均時間也在一毫秒之下。這是http協議無法達到的速度,如果你在瀏覽器的F12的視窗觀察過,一個http協議呼叫如果整體花費的時間在5毫秒甚至10毫秒,那麼其實就可以認為這個http請求響應時間是很短的了。

所以絕大部分公司內部的系統之間通訊都會採用rpc呼叫這種方式。這裡不要擡槓,如果你的公司內部系統通訊採用的是基於http協議的,那說明你們的系統很有可能沒有效能的要求。

RPC呼叫雖然簡化了擼碼的難度,但是想要實現一套rpc框架,何止容易,一套優秀的rpc框架,更是難如登天。

01
連線服務

多數rpc框架的服務端以service的方式來執行,為了避免和其他程序發生監聽埠的衝突,一般會隨機選擇一個埠來進行監聽。雖然這看上去很好,但是卻給client端帶來了麻煩,如果服務端監聽固定埠,client連線服務端的時候,最少可以在程式碼中固定寫死服務端的IP和埠。但是現在服務端監聽的埠是隨機的,而且更可怕的是伺服器有可能會更換或者切換IP,那client怎麼才能正確的去和服務端建立連線呢?

服務端之所以會採用這種隨機方式來監聽埠,其中很大一個原因是為了以後擴容。client如何正確的去連線伺服器則採用了一個集中式的方案,服務端引入了一個服務註冊中心的概念,有的系統可能會以別的名稱來體現,但是作用是類似的。這個註冊中心儲存著所有的服務端資訊,其中包括每個服務端的IP和埠,有的甚至還有版本資訊,每個服務端程序啟動的時候,都是採用主動連線註冊中心,主動註冊的方式。client端在發起連線服務的時候,首先去註冊中心查詢已經註冊的服務端資訊,然後進行連線。這樣rpc呼叫在某種程度上在連線步驟就實現了“自動化”。

02
呼叫方法

當client和服務端建立tcp連線之後(有的rpc框架會採用udp協議),下一個問題就是client和服務端怎麼相認的問題了。舉個栗子:客戶端想要實現一個獲取使用者姓名的方法,方法名怎麼定義才能讓服務端正確識別出來呢?是傳一個字串“GetName”,還是傳一個整數1來代表呢?服務端的返回結果,如果發生異常改如何返回呢?

當我們在本地呼叫一個函式,語法,語義,以及語法語義的分析,編譯器已經幫我們做好了這些,但是rpc是遠端過程呼叫,雖然表面上和本地類似,但是已經出現了跨網路的情況,語法語義等等這些分析需要client和服務端協商一致。

其實現代幾乎大部分rpc通訊都遵循一個標準:

當client發起一個遠端呼叫的時候,它首先會先呼叫本地的Stub,它負責將呼叫的介面,函式以及引數按照約定好的協議格式進行編碼,然後通過本地的Runtime進行傳輸,最後通過網絡卡將資料包傳送到指定的伺服器。

伺服器Runtime接收到請求之後,會首先呼叫本地的Stub按照約定好的協議格式進行解碼,最後呼叫服務端具體的函式。函式執行完畢,把結果利用本地的Stub編碼之後通過runtime傳送給客戶端。客戶端Runtime接收到訊息利用本地Stub進行解碼,然後進行其他處理。

由此可見,現代的rpc框架其實是把協議的封裝和資料的傳送分別抽象成了單獨的層。Stub負責協議部分,Runtime處理資料傳送以及網路相關部分。

03
網路資料傳輸

資料通過網路傳輸過程中,每個資料包的完整性如何來識別,如果是一個簡單int型資料很簡單,但是如果是一個類或者一個數組,甚至是其他變長的型別,rpc的通訊協議如何約束這些,如果能正確識別出來資料是協議部分最難處理的部分。更何況還有大頭小頭編碼的問題。

凡是基於網路傳輸的形式,任何通訊都是不可靠的,網路本質是不可靠的。包括網路抖動,錯誤等造成的丟包,粘包現象,如何正確的處理也是一個rpc通訊中很重要的部分。一個rpc請求失敗,是直接丟棄還是重試,這些策略都需要去規定。

04
效能

1.一個rpc呼叫如果採用同步的方式,效能會大大打折扣,如何實現rpc的非同步呼叫,這是一個rpc是否優秀的重要指標。

2.無論rpc的網路傳輸多麼優秀,都會有效能損耗,能否把某些結果資料設定快取?

3.無論是client還是服務端,處理請求的執行緒能否重用(執行緒池)?

4.能否支援多語言呢?

socket雖易,RPC卻難