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

640?wx_fmt=gif

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

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg

http協議不也是利用的Socket嗎

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg

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

640
640?wx_fmt=jpeg
640?wx_fmt=png
640?wx_fmt=png

RPC是遠程過程調用(Remote Procedure Call)的縮寫形式,是在多任務操作系統或聯網的計算機之間運行的程序和進程所用的通信技術

01
開局
640

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

640

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

640
640

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

01
連接服務
640

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

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

02
調用方法
640

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

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

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

640?wx_fmt=jpeg

當client發起一個遠程調用的時候,它首先會先調用本地的Stub,它負責將調用的接口,函數以及參數按照約定好的協議格式進行編碼,然後通過本地的Runtime進行傳輸,最後通過網卡將數據包發送到指定的服務器。

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

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

03
網絡數據傳輸

640

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

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

04
性能

640

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

2.無論rpc的網絡傳輸多麼優秀,都會有性能損耗,能否把某些結果數據設置緩存?

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

4.能否支持多語言呢?

640

socket雖易,RPC卻難


640?wx_fmt=gif

關注後回覆:“大禮包”和“福利”,領取驚喜

長按識別二維碼關注

640?wx_fmt=jpeg
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章