程序員修神之路--設計一套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卻難



posted @ 2019-10-14 09:25 架構師修行之路 閱讀(...) 評論(...) 編輯 收藏
ag二分彩