gRPC實戰包含一系列文章,包括原創和翻譯。最終會造成一個完整的系列,後續會不斷完善,增長新的內容:算法
=============================================================
RPC許多剛剛接觸gRPC用戶或是剛剛把gRPC服務部署到kubernetes中感到驚訝的是,發現Kubernetes的默認負載平衡一般沒法與gRPC一塊兒使用。例如,當您使用一個簡單的gRPC Node.js微服務應用程序並將其部署在Kubernetes上時,將發生如下狀況:segmentfault
儘管此處顯示的服務具備多個Pod,但從Kubernetes的CPU圖形能夠清楚地看到,只有一個Pod實際上正在執行全部工做-由於只有一個Pod正在接收任何流量。爲何?後端
gRPC使用性能加強的HTTP/2協議。 HTTP/2具有更低的延遲的特色,實際上是經過利用單個長期存在的TCP鏈接並在其上多路複用請求/響應。這會給第4層(L4)負載均衡器帶來問題,由於它們的級別過低,沒法根據接收到的流量類型作出路由決策。這樣,嘗試對HTTP/2流量進行負載平衡的L4負載平衡器將打開一個TCP鏈接,並將全部連續的流量路由到該相同的長期鏈接,從而實際上取消了負載平衡。服務器
Kubernetes的kube-proxy本質上是一個L4負載平衡器,所以咱們不能依靠它來平衡微服務之間的gRPC調用。架構
實際上gRPC負載均衡有如下兩種實現方式:app
在代理負載平衡與客戶端負載平衡之間進行選擇實際上是結構的選擇。在代理負載平衡中,客戶端將RPC發送給負載平衡器(LB)代理。 LB將RPC調用分發到實現了實際服務調用邏輯的可用後端服務器之一。 LB跟蹤每一個後端的負載,並實現用於公平分配負載的算法。客戶端自己不瞭解後端服務器。客戶可能不受信任。此體系結構一般用於面向用戶的服務,開放式Internet的客戶端能夠將其鏈接到數據中心中的服務器,以下圖所示。在這種狀況下,客戶端向LB(#1)發出請求。 LB將請求傳遞給後端之一(#2),後端將報告加載到LB(#3)。負載均衡
在客戶端負載平衡中,客戶端知道多個後端服務器,併爲每一個RPC選擇一個。客戶端從後端服務器獲取負載報告,而且客戶端實施負載平衡算法。在更簡單的配置中,不考慮服務器負載,客戶端只能在可用服務器之間進行輪詢。以下圖所示。如您所見,客戶端向特定後端(#1)發出請求。後端一般在執行客戶端RPC的同一鏈接上以負載信息(#2)進行響應。客戶端而後更新其內部狀態。less
下邊是兩種模式的對比分析:ide
Proxy | Client Side | |
---|---|---|
優點 | 客戶端能夠專心業務邏輯,無需感知後端,與不受信任的客戶端一塊兒使用 | 高性能,由於少了一層代理 |
劣勢 | 延遲更高,LB吞吐量可能會限制可伸縮性 | 客戶端變複雜,客戶端跟蹤服務器的負載和運行情況,客戶端實現負載平衡算法,每一個語言的實現和維護負擔,客戶端須要被信任,或者信任邊界須要由後備LB處理。 |
使用gRPC客戶端負載平衡器,該負載平衡器被嵌入到gRPC客戶端庫中。這樣,每一個客戶端微服務均可以執行本身的負載平衡。可是,最終的客戶很是脆弱,須要大量的自定義代碼來提供任何形式的彈性,指標或日誌記錄,全部這些咱們都須要針對管道中使用的每種不一樣語言重複屢次。模塊化
並且在雲原生時代,整個基礎架構升級,表現爲
選擇代理的負責均衡模式,對於擁抱雲原生的公司更加有意義和可拓展性。
咱們真正須要的是更智能的負載均衡器。
咱們須要第7層(L7)負載平衡器,由於它們在應用程序層運行,而且能夠檢查流量以作出路由決策。最重要的是,它們能夠支持HTTP/2協議。
L7負載平衡器有不少不一樣的選項,包括NGINX和HAProxy,但事實證實,大多數選項太過沉重,沒法輕鬆加入咱們的微服務架構。咱們將選擇權縮減爲兩個關鍵競爭者-Envoy和Linkerd。二者都是在考慮微服務架構的狀況下開發的,而且都支持gRPC。
雖然兩個代理都有許多理想的功能,但咱們最終的決定權仍是取決於代理的範圍。爲此,有一個明顯的贏家。特使很小。它使用C ++ 11編寫,沒有基於Java的Linkerd附帶的企業級功能。
肯定Envoy以後,咱們便開始深刻研究其功能集,而且有不少值得一看的地方。
Envoy由Lyft編寫和開源,是多年來與一般在微服務體系結構中發生的複雜路由問題做鬥爭的直接結果。它實質上是爲解決咱們的問題而設計的,而且具備:
並且新版本的envoy將會支持wasm,意味着將來的不少擴展能夠直接使用你熟悉的語言來完成,並且具有原生的性能。
在您的架構中,若是暫時沒有使用service mesh。能夠採起下面的方案。
在Kubernetes中,一組一個或多個容器被稱爲Pod。能夠複製Pod以提供擴展,並封裝在稱爲服務的抽象中,這些抽象爲訪問基礎Pod提供了穩定的IP地址。從Kubernetes 1.2開始,請求服務IP的默認行爲是將返回一個隨機的後端Pod。可是,您能夠將服務從新配置爲headless的,以便服務IP將返回可用的pod IP的整個列表,從而使您可以執行本身的服務發現。
Envoy被設計爲做爲Sidecar容器運行,並與客戶端容器並排放置,以模塊化的方式補充了其功能。在Kubernetes中,這轉換爲在同一容器中運行客戶端容器和Envoy容器。咱們將服務配置爲headless的,以便爲Envoy提供端點以用於服務發現。並且因爲Envoy輸出了大量指標,所以咱們可以輕鬆觀察到連續gRPC調用的循環負載平衡,以確認其按預期運行。
最終效果以下:
固然最終的方案是service mesh。除了能夠解決gRPC負載均衡的問題,會帶來更多的優點。