7天用Go動手寫/從零實現RPC框架GeeRPC

極客兔兔發表於2020-10-16

在這裡插入圖片描述

0 目錄

1 談談 RPC 框架

RPC(Remote Procedure Call,遠端過程呼叫)是一種計算機通訊協議,允許呼叫不同程式空間的程式。RPC 的客戶端和伺服器可以在一臺機器上,也可以在不同的機器上。程式設計師使用時,就像呼叫本地程式一樣,無需關注內部的實現細節。

不同的應用程式之間的通訊方式有很多,比如瀏覽器和伺服器之間廣泛使用的基於 HTTP 協議的 Restful API。與 RPC 相比,Restful API 有相對統一的標準,因而更通用,相容性更好,支援不同的語言。HTTP 協議是基於文字的,一般具備更好的可讀性。但是缺點也很明顯:

  • Restful 介面需要額外的定義,無論是客戶端還是服務端,都需要額外的程式碼來處理,而 RPC 呼叫則更接近於直接呼叫。
  • 基於 HTTP 協議的 Restful 報文冗餘,承載了過多的無效資訊,而 RPC 通常使用自定義的協議格式,減少冗餘報文。
  • RPC 可以採用更高效的序列化協議,將文字轉為二進位制傳輸,獲得更高的效能。
  • 因為 RPC 的靈活性,所以更容易擴充套件和整合諸如註冊中心、負載均衡等功能。

2 RPC 框架需要解決什麼問題

RPC 框架需要解決什麼問題?或者我們換一個問題,為什麼需要 RPC 框架?

我們可以想象下兩臺機器上,兩個應用程式之間需要通訊,那麼首先,需要確定採用的傳輸協議是什麼?如果這個兩個應用程式位於不同的機器,那麼一般會選擇 TCP 協議或者 HTTP 協議;那如果兩個應用程式位於相同的機器,也可以選擇 Unix Socket 協議。傳輸協議確定之後,還需要確定報文的編碼格式,比如採用最常用的 JSON 或者 XML,那如果報文比較大,還可能會選擇 protobuf 等其他的編碼方式,甚至編碼之後,再進行壓縮。接收端獲取報文則需要相反的過程,先解壓再解碼。

解決了傳輸協議和報文編碼的問題,接下來還需要解決一系列的可用性問題,例如,連線超時了怎麼辦?是否支援非同步請求和併發?

如果服務端的例項很多,客戶端並不關心這些例項的地址和部署位置,只關心自己能否獲取到期待的結果,那就引出了註冊中心(registry)和負載均衡(load balance)的問題。簡單地說,即客戶端和服務端互相不感知對方的存在,服務端啟動時將自己註冊到註冊中心,客戶端呼叫時,從註冊中心獲取到所有可用的例項,選擇一個來呼叫。這樣服務端和客戶端只需要感知註冊中心的存在就夠了。註冊中心通常還需要實現服務動態新增、刪除,使用心跳確保服務處於可用狀態等功能。

再進一步,假設服務端是不同的團隊提供的,如果沒有統一的 RPC 框架,各個團隊的服務提供方就需要各自實現一套訊息編解碼、連線池、收發執行緒、超時處理等“業務之外”的重複技術勞動,造成整體的低效。因此,“業務之外”的這部分公共的能力,即是 RPC 框架所需要具備的能力。

3 關於 GeeRPC

Go 語言廣泛地應用於雲端計算和微服務,成熟的 RPC 框架和微服務框架汗牛充棟。grpcrpcxgo-micro 等都是非常成熟的框架。一般而言,RPC 是微服務框架的一個子集,微服務框架可以自己實現 RPC 部分,當然,也可以選擇不同的 RPC 框架作為通訊基座。

考慮效能和功能,上述成熟的框架程式碼量都比較龐大,而且通常和第三方庫,例如 protobufetcdzookeeper 等有比較深的耦合,難以直觀地窺視框架的本質。GeeRPC 的目的是以最少的程式碼,實現 RPC 框架中最為重要的部分,幫助大家理解 RPC 框架在設計時需要考慮什麼。程式碼簡潔是第一位的,功能是第二位的。

因此,GeeRPC 選擇從零實現 Go 語言官方的標準庫 net/rpc,並在此基礎上,新增了協議交換(protocol exchange)、註冊中心(registry)、服務發現(service discovery)、負載均衡(load balance)、超時處理(timeout processing)等特性。分七天完成,最終程式碼約 1000 行。

附 推薦閱讀

原文地址: 7天用Go從零實現RPC框架GeeRPC - 極客兔兔
關注知乎: 極客兔兔
關注微博: @極客兔兔

相關文章