?gRPC 學(xué)習(xí)筆記,記錄gprc一些基本概念.
?gRPC正如其他 RPC 系統(tǒng)捧弃,gRPC 基于如下思想:定義一個服務(wù)彩掐,指定其可以被遠程調(diào)用的方法及其參數(shù)和返回類型焕数。gRPC 默認使用 protocol buffers 作為接口定義語言泽疆,來描述服務(wù)接口和有效載荷消息結(jié)構(gòu)要门。如果有需要的話,可以使用其他替代方案胚想。
gRPC是基于HTTP/2協(xié)議的,要深刻理解 gRPC協(xié)議,就有必要理解一下 HTTP/2協(xié)議.
gRPC是什么
gRPC 一開始由 google 開發(fā)琐凭,是一款語言中立芽隆、平臺中立浊服、開源的遠程過程調(diào)用(RPC)系統(tǒng).
在 gRPC 里客戶端應(yīng)用可以像調(diào)用本地對象一樣直接調(diào)用另一臺不同的機器上服務(wù)端應(yīng)用的方法,使得您能夠更容易地創(chuàng)建分布式應(yīng)用和服務(wù)胚吁。與許多 RPC 系統(tǒng)類似牙躺,gRPC 也是基于以下理念:定義一個服務(wù),指定其能夠被遠程調(diào)用的方法(包含參數(shù)和返回類型)腕扶。在服務(wù)端實現(xiàn)這個接口孽拷,并運行一個 gRPC 服務(wù)器來處理客戶端調(diào)用。在客戶端擁有一個存根能夠像服務(wù)端一樣的方法半抱。
gRPC 客戶端和服務(wù)端可以在多種環(huán)境中運行和交互 - 從 google 內(nèi)部的服務(wù)器到你自己的筆記本脓恕,并且可以用任何 gRPC 支持的語言來編寫。所以窿侈,你可以很容易地用 Java 創(chuàng)建一個 gRPC 服務(wù)端炼幔,用 Go、Python史简、Ruby 來創(chuàng)建客戶端乃秀。此外,Google 最新 API 將有 gRPC 版本的接口圆兵,使你很容易地將 Google 的功能集成到你的應(yīng)用里跺讯。
更多跨語言跨平臺支持請看這里
gRPC架構(gòu)
如上圖所示,gRPC 的代碼結(jié)構(gòu)主要分為三層:
- 運輸層殉农,最低層刀脏,基于 Http2.0 和 SSL。
- 通道層(Channel層)超凳,核心的 C 代碼愈污,實現(xiàn)與運輸層的交互。
- 應(yīng)用層聪建,各種語言的實現(xiàn)钙畔,實現(xiàn)各種語言調(diào)用通道層的 C 代碼。
PS: gRPC 的 Java 和 Go 語言版本不由上面架構(gòu)實現(xiàn)金麸,Java 和 Go 有自己的高性能網(wǎng)絡(luò)庫擎析。
protocol buffers
gRPC 默認使用 protocol buffers,這是 Google 開源的一套成熟的結(jié)構(gòu)數(shù)據(jù)序列化機制(當(dāng)然也可以使用其他數(shù)據(jù)格式如 JSON)。你可以用 proto files 創(chuàng)建 gRPC 服務(wù)揍魂,用 protocol buffers 消息類型來定義方法參數(shù)和返回類型桨醋。
服務(wù)定義
正如其他 RPC 系統(tǒng),gRPC 基于如下思想:定義一個服務(wù)现斋, 指定其可以被遠程調(diào)用的方法及其參數(shù)和返回類型喜最。gRPC 默認使用 protocol buffers 作為接口定義語言,來描述服務(wù)接口和有效載荷消息結(jié)構(gòu)庄蹋。如果有需要的話瞬内,可以使用其他替代方案。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
required string greeting = 1;
}
message HelloResponse {
required string reply = 1;
}
gRPC 允許你定義四類服務(wù)方法:
- 單項 RPC限书,即客戶端發(fā)送一個請求給服務(wù)端虫蝶,從服務(wù)端獲取一個應(yīng)答,就像一次普通的函數(shù)調(diào)用倦西。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- 服務(wù)端流式 RPC能真,即客戶端發(fā)送一個請求給服務(wù)端,可獲取一個數(shù)據(jù)流用來讀取一系列消息扰柠》垲恚客戶端從返回的數(shù)據(jù)流里一直讀取直到?jīng)]有更多消息為止。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- 客戶端流式 RPC卤档,即客戶端用提供的一個數(shù)據(jù)流寫入并發(fā)送一系列消息給服務(wù)端蝙泼。一旦客戶端完成消息寫入,就等待服務(wù)端讀取這些消息并返回應(yīng)答裆装。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 雙向流式 RPC踱承,即兩邊都可以分別通過一個讀寫數(shù)據(jù)流來發(fā)送一系列消息。這兩個數(shù)據(jù)流操作是相互獨立的哨免,所以客戶端和服務(wù)端能按其希望的任意順序讀寫茎活,例如:服務(wù)端可以在寫應(yīng)答前等待所有的客戶端消息,或者它可以先讀一個消息再寫一個消息琢唾,或者是讀寫相結(jié)合的其他方式载荔。每個數(shù)據(jù)流里消息的順序會被保持。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
使用 API 接口
gRPC 提供 protocol buffer 編譯插件采桃,能夠從一個服務(wù)定義的 .proto 文件生成客戶端和服務(wù)端代碼懒熙。通常 gRPC 用戶可以在服務(wù)端實現(xiàn)這些API,并從客戶端調(diào)用它們普办。
- 在服務(wù)側(cè)工扎,服務(wù)端實現(xiàn)服務(wù)接口,運行一個 gRPC 服務(wù)器來處理客戶端調(diào)用衔蹲。gRPC 底層架構(gòu)會解碼傳入的請求肢娘,執(zhí)行服務(wù)方法,編碼服務(wù)應(yīng)答。
- 在客戶側(cè)橱健,客戶端有一個存根實現(xiàn)了服務(wù)端同樣的方法而钞。客戶端可以在本地存根調(diào)用這些方法拘荡,用合適的 protocol buffer 消息類型封裝這些參數(shù)— gRPC 來負責(zé)發(fā)送請求給服務(wù)端并返回服務(wù)端 protocol buffer 響應(yīng)臼节。
同步 vs 異步
同步 RPC 調(diào)用一直會阻塞直到從服務(wù)端獲得一個應(yīng)答,這與 RPC 希望的抽象最為接近珊皿。另一方面網(wǎng)絡(luò)內(nèi)部是異步的网缝,并且在許多場景下能夠在不阻塞當(dāng)前線程的情況下啟動 RPC 是非常有用的。
在多數(shù)語言里亮隙,gRPC 編程接口同時支持同步和異步的特點途凫。
RPC 生命周期
現(xiàn)在讓我們來仔細了解一下當(dāng) gRPC 客戶端調(diào)用 gRPC 服務(wù)端的方法時到底發(fā)生了什么垢夹。
單項 RPC
首先我們來了解一下最簡單的 RPC 形式:客戶端發(fā)出單個請求溢吻,獲得單個響應(yīng)。
一旦客戶端通過樁調(diào)用一個方法果元,服務(wù)端會得到相關(guān)通知 促王,通知包括客戶端的元數(shù)據(jù),方法名而晒,允許的響應(yīng)期限(如果可以的話)
服務(wù)端既可以在任何響應(yīng)之前直接發(fā)送回初始的元數(shù)據(jù)蝇狼,也可以等待客戶端的請求信息,到底哪個先發(fā)生倡怎,取決于具體的應(yīng)用.
一旦服務(wù)端獲得客戶端的請求信息迅耘,就會做所需的任何工作來創(chuàng)建或組裝對應(yīng)的響應(yīng)。如果成功的話监署,這個響應(yīng)會和包含狀態(tài)碼以及可選的狀態(tài)信息等狀態(tài)明細及可選的追蹤信息返回給客戶端 颤专。
假如狀態(tài)是 OK 的話,客戶端會得到應(yīng)答钠乏,這將結(jié)束客戶端的調(diào)用栖秕。
服務(wù)端流式 RPC
服務(wù)端流式 RPC 除了在得到客戶端請求信息后發(fā)送回一個應(yīng)答流之外,與我們的簡單例子一樣晓避。在發(fā)送完所有應(yīng)答后簇捍,服務(wù)端的狀態(tài)詳情(狀態(tài)碼和可選的狀態(tài)信息)和可選的跟蹤元數(shù)據(jù)被發(fā)送回客戶端,以此來完成服務(wù)端的工作俏拱∈钏埽客戶端在接收到所有服務(wù)端的應(yīng)答后也完成了工作。
客戶端流式 RPC
客戶端流式 RPC 也基本與我們的簡單例子一樣锅必,區(qū)別在于客戶端通過發(fā)送一個請求流給服務(wù)端事格,取代了原先發(fā)送的單個請求。服務(wù)端通常(但并不必須)會在接收到客戶端所有的請求后發(fā)送回一個應(yīng)答,其中附帶有它的狀態(tài)詳情和可選的跟蹤數(shù)據(jù)分蓖。
雙向流式 RPC
雙向流式 RPC 尔艇,調(diào)用由客戶端調(diào)用方法來初始化,而服務(wù)端則接收到客戶端的元數(shù)據(jù)么鹤,方法名和截止時間终娃。服務(wù)端可以選擇發(fā)送回它的初始元數(shù)據(jù)或等待客戶端發(fā)送請求。
下一步怎樣發(fā)展取決于應(yīng)用蒸甜,因為客戶端和服務(wù)端能在任意順序上讀寫 - 這些流的操作是完全獨立的棠耕。例如服務(wù)端可以一直等直到它接收到所有客戶端的消息才寫應(yīng)答,或者服務(wù)端和客戶端可以像"乒乓球"一樣:服務(wù)端后得到一個請求就回送一個應(yīng)答柠新,接著客戶端根據(jù)應(yīng)答來發(fā)送另一個請求窍荧,以此類推。
截止時間
gRPC 允許客戶端在調(diào)用一個遠程方法前指定一個最后期限值恨憎。這個值指定了在客戶端可以等待服務(wù)端多長時間來應(yīng)答蕊退,超過這個時間值 RPC 將結(jié)束并返回DEADLINE_EXCEEDED錯誤。在服務(wù)端可以查詢這個期限值來看是否一個特定的方法已經(jīng)過期憔恳,或者還剩多長時間來完成這個方法瓤荔。
各語言來指定一個截止時間的方式是不同的 - 比如在 Python 里一個截止時間值總是必須的,但并不是所有語言都有一個默認的截止時間钥组。
RPC 終止
在 gRPC 里输硝,客戶端和服務(wù)端對調(diào)用成功的判斷是獨立的、本地的程梦,他們的結(jié)論可能不一致点把。這意味著,比如你有一個 RPC 在服務(wù)端成功結(jié)束("我已經(jīng)返回了所有應(yīng)答!")屿附,到那時在客戶端可能是失敗的("應(yīng)答在最后期限后才來到!")郎逃。也可能在客戶端把所有請求發(fā)送完前,服務(wù)端卻判斷調(diào)用已經(jīng)完成了拿撩。
取消 RPC
無論客戶端還是服務(wù)端均可以再任何時間取消一個 RPC 衣厘。一個取消會立即終止 RPC 這樣可以避免更多操作被執(zhí)行。它不是一個"撤銷"压恒, 在取消前已經(jīng)完成的不會被回滾影暴。當(dāng)然,通過同步調(diào)用的 RPC 不能被取消探赫,因為直到 RPC 結(jié)束前型宙,程序控制權(quán)還沒有交還給應(yīng)用。
元數(shù)據(jù)集
元數(shù)據(jù)是一個特殊 RPC 調(diào)用對應(yīng)的信息(授權(quán)詳情]) 伦吠,這些信息以鍵值對的形式存在妆兑,一般鍵的類型是字符串魂拦,值的類型一般也是字符串(當(dāng)然也可以是二進制數(shù)據(jù))。元數(shù)據(jù)對 gRPC 本事來說是不透明的 - 它讓客戶端提供調(diào)用相關(guān)的信息給服務(wù)端搁嗓,反之亦然芯勘。
對于元數(shù)據(jù)的訪問是語言相關(guān)的。
channel
在創(chuàng)建客戶端存根時腺逛,一個 gRPC channel 提供一個特定主機和端口服務(wù)端的連接荷愕。客戶端可以通過指定 channel 參數(shù)來修改 gRPC 的默認行為棍矛,比如打開關(guān)閉消息壓縮安疗。一個 chennel 具有狀態(tài),包含已連接和空閑 够委。
gRPC 如何處理關(guān)閉 channel 是語言相關(guān)的荐类。有些語言可允許詢問頻道狀態(tài)。