當前位置:
首頁 > 最新 > RPC理念,高性能RPC框架gRpc核心概念及示例

RPC理念,高性能RPC框架gRpc核心概念及示例

RPC理念

RPC(Remote Procedure Call)即遠程過程調用,RPC框架的目標就是讓遠程服務調用更加簡單,透明,RPC框架負責屏蔽底層的傳輸方式(TCP或UDP),序列化方式(XML/JSON/二進位)和通信細節,服務調用者可以像調用本地介面一樣調用遠程服務的提供者,而不必關心底層通信細節和調用過程,RPC調用的基本原理圖:

圖注:RPC調用基本原理

一次完整的RPC調用流程(同步調用,非同步另說)如下:

1)服務消費方(client)調用以本地調用方式調用服務;

2)client stub接收到調用後負責將方法、參數等組裝成能夠進行網路傳輸的消息體;

3)client stub找到服務地址,並將消息發送到服務端;

4)server stub收到消息後進行解碼;

5)server stub根據解碼結果調用本地的服務;

6)本地服務執行並將結果返回給server stub;

7)server stub將返回結果打包成消息並發送至消費方;

8)client stub接收到消息,並進行解碼;

9)服務消費方得到最終結果。

RPC框架的目標就是要2~8這些步驟都封裝起來,讓用戶對這些細節透明。

目前在RPC領域可謂百花齊放,各大廠均有重複造輪子的跡象,業界主流的RPC框架主要分三類:

只支持特定語言的RPC框架,例如新浪微博的Moton

支持多種語言RPC框架,如Google的gRpc,Facebook的Thrift

支持服務治理等服務化特性的分散式服務框架,底層仍然是RPC框架,如阿里的Dubbo,華為的ServiceComb,目前這兩項目均在Apache的孵化器中孵化

gRpc簡介

gRPC 是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持

gRPC 基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間佔用,gRpc採用Google內部長期使用,經大量生產流量驗證的protobuf序列化,具有很高的性能,有了 gRPC, 我們可以一次性的在一個 .proto 文件中定義服務並使用任何支持它的語言去實現客戶端和伺服器,反過來,它們可以在各種環境中,從Google的伺服器到你自己的平板電腦—— gRPC 幫你解決了不同語言及環境間通信的複雜性。使用 protocol buffers 還能獲得其他好處,包括高效的序列號,簡單的 IDL 以及容易進行介面更新

在 gRPC 里客戶端應用可以像調用本地對象一樣直接調用另一台不同的機器上服務端應用的方法,使得您能夠更容易地創建分散式應用和服務。與許多 RPC 系統類似,gRPC 也是基於以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個介面,並運行一個 gRPC 伺服器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。

圖注:gRpc調用原理

gRPC 客戶端和服務端可以在多種環境中運行和交互 - 從 google 內部的伺服器到你自己的筆記本,並且可以用任何 gRPC 支持的語言來編寫。所以,你可以很容易地用 Java 創建一個 gRPC 服務端,用 Go、Python、Ruby 來創建客戶端。此外,Google 最新 API 將有 gRPC 版本的介面,使你很容易地將 Google 的功能集成到你的應用里

另外在學習gRpc是強烈建議掌握的知識點:

JavaNIO

Netty

Google protobuff

Http2協議

Reactor模式

多線程

gRpc核心概念

注:本文列舉的概念並不是全部的gRpc概念,如需了解更多,請參閱其官網

gRPc官網文檔:https://grpc.io/docs/guides/


定義服務

正如其他 RPC 系統,gRPC 基於如下思想:定義一個服務, 指定其可以被遠程調用的方法及其參數和返回類型。gRPC 默認使用 protocol buffers 作為介面定義語言(如果尚不熟悉protobuff,可以先參考Google protobuff, protocol-buffers文檔:https://developers.google.com/protocol-buffers/),來描述服務介面和有效載荷消息結構。如果有需要的話,可以使用其他替代方案。

gRPC 允許你定義四類服務方法:

單項 RPC,即客戶端發送一個請求給服務端,從服務端獲取一個應答,就像一次普通的函數調用。

服務端流式 RPC,即客戶端發送一個請求給服務端,可獲取一個數據流用來讀取一系列消息。客戶端從返回的數據流里一直讀取直到沒有更多消息為止。

客戶端流式 RPC,即客戶端用提供的一個數據流寫入並發送一系列消息給服務端。一旦客戶端完成消息寫入,就等待服務端讀取這些消息並返回應答。

雙向流式 RPC,即兩邊都可以分別通過一個讀寫數據流來發送一系列消息。這兩個數據流操作是相互獨立的,所以客戶端和服務端能按其希望的任意順序讀寫,例如:服務端可以在寫應答前等待所有的客戶端消息,或者它可以先讀一個消息再寫一個消息,或者是讀寫相結合的其他方式。每個數據流里消息的順序會被保持。


使用 API 介面

gRPC 提供 protocol buffer 編譯插件,能夠從一個服務定義的 .proto 文件生成客戶端和服務端代碼。通常 gRPC 用戶可以在服務端實現這些API,並從客戶端調用它們。

在服務側,服務端實現服務介面,運行一個 gRPC 伺服器來處理客戶端調用。gRPC 底層架構會解碼傳入的請求,執行服務方法,編碼服務應答。

在客戶側,客戶端有一個存根實現了服務端同樣的方法。客戶端可以在本地存根調用這些方法,用合適的 protocol buffer 消息類型封裝這些參數— gRPC 來負責發送請求給服務端並返回服務端 protocol buffer 響應。


RPC 生命周期

現在讓我們來仔細了解一下當 gRPC 客戶端調用 gRPC 服務端的方法時到底發生了什麼


首先我們來了解一下最簡單的 RPC 形式:客戶端發出單個請求,獲得單個響應。

一旦客戶端通過樁調用一個方法,服務端會得到相關通知 ,通知包括客戶端的元數據,方法名,允許的響應期限(如果可以的話)

服務端既可以在任何響應之前直接發送回初始的元數據,也可以等待客戶端的請求信息,到底哪個先發生,取決於具體的應用。

一旦服務端獲得客戶端的請求信息,就會做所需的任何工作來創建或組裝對應的響應。如果成功的話,這個響應會和包含狀態碼以及可選的狀態信息等狀態明細及可選的追蹤信息返回給客戶端 。

假如狀態是 OK 的話,客戶端會得到應答,這將結束客戶端的調用。


服務端流式 RPC 除了在得到客戶端請求信息後發送回一個應答流之外,與我們的簡單例子一樣。在發送完所有應答後,服務端的狀態詳情(狀態碼和可選的狀態信息)和可選的跟蹤元數據被發送回客戶端,以此來完成服務端的工作。客戶端在接收到所有服務端的應答後也完成了工作。


客戶端流式 RPC 也基本與我們的簡單例子一樣,區別在於客戶端通過發送一個請求流給服務端,取代了原先發送的單個請求。服務端通常(但並不必須)會在接收到客戶端所有的請求後發送回一個應答,其中附帶有它的狀態詳情和可選的跟蹤數據。


雙向流式 RPC ,調用由客戶端調用方法來初始化,而服務端則接收到客戶端的元數據,方法名和截止時間。服務端可以選擇發送回它的初始元數據或等待客戶端發送請求。

下一步怎樣發展取決於應用,因為客戶端和服務端能在任意順序上讀寫 - 這些流的操作是完全獨立的。例如服務端可以一直等直到它接收到所有客戶端的消息才寫應答,或者服務端和客戶端可以像"乒乓球"一樣:服務端後得到一個請求就回送一個應答,接著客戶端根據應答來發送另一個請求,以此類推。

gRpc示例

本示例模擬了日常開發中,不同服務間的間的調用,具有很強的實戰性,項目代碼已上傳至githug,如有需要clone到本地及可運行

grpc-api:存放由.proto文件生產的服務介面及實體類,因為客戶端及服務端均需要此模塊,故將其抽取成公共模塊(此處注意:proto編譯器能生成我們需要的文件,但每次執行命令是比較煩人的事情,maven中有專門生產protobuff文件的插件,根據.proto文件生成需要的服務介面及實體類的工程我已上傳到github,如需要自取: https://github.com/JavaDebugMan/Protobuf)

grpc-commom:公共模塊

grpc-task:客戶端模塊

grpc-user:服務端模塊


服務端

服務端的工作有兩個:

1.實現我們服務定義的生成的服務介面:做我們的服務的實際的「工作」。

2.運行一個 gRPC 伺服器,監聽來自客戶端的請求並返回服務的響應。

實現 GreeterGrpc.GreeterImplBase


客戶端

為了調用服務方法,我們需要首先創建一個 存根,或者兩個存根:

一個 阻塞/同步 存根:這意味著 RPC 調用等待伺服器響應,並且要麼返回應答,要麼造成異常。

一個 非阻塞/非同步 存根可以向伺服器發起非阻塞調用,應答會非同步返回。你可以使用非同步存根去發起特定類型的流式調用。

我們首先為存根創建一個 gRPC channel,指明伺服器地址和我們想連接的埠號:

如你所見,我們用一個 NettyServerBuilder 構建和啟動伺服器。這個伺服器的生成器基於 Netty 傳輸框架。

我們使用 Netty 傳輸框架,所以我們用一個 NettyServerBuilder 啟動伺服器。


服務端:

可以發現服務端已在50051埠監聽客戶端連接

客戶端:

運行客戶端已答應出我們希望出現的Hello world


gRPC開源組件官方並未直接提供服務註冊與發現的功能實現,但其設計文檔已提供實現的思路,並在不同語言的gRPC代碼API中已提供了命名解析和負載均衡介面供擴展

我們完全可以使用etcd,consul等註冊中心實現gRpc服務的註冊於發現及 負載均衡


github上已有大牛做了相關的starter,可以直接與gRpc在項目中無縫集成 地址:

https://github.com/LogNet/grpc-spring-boot-starter

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 架構師是怎樣煉成的 的精彩文章:

TAG:架構師是怎樣煉成的 |