> From: http://doc.oschina.net/grpc?t=58009
## 概覽
### 服務定義
正如其他 RPC 系統,gRPC 基于如下思想:定義一個服務, 指定其可以被遠程調用的方法及其參數和返回類型。gRPC 默認使用 [protocol buffers](https://developers.google.com/protocol-buffers/) 作為接口定義語言,來描述服務接口和有效載荷消息結構。如果有需要的話,可以使用其他替代方案。
~~~
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
required string greeting = 1;
}
message HelloResponse {
required string reply = 1;
}
~~~
gRPC 允許你定義四類服務方法:
* 單項 RPC,即客戶端發送一個請求給服務端,從服務端獲取一個應答,就像一次普通的函數調用。
~~~
rpc SayHello(HelloRequest) returns (HelloResponse){
}
~~~
* 服務端流式 RPC,即客戶端發送一個請求給服務端,可獲取一個數據流用來讀取一系列消息。客戶端從返回的數據流里一直讀取直到沒有更多消息為止。
~~~
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
~~~
* 客戶端流式 RPC,即客戶端用提供的一個數據流寫入并發送一系列消息給服務端。一旦客戶端完成消息寫入,就等待服務端讀取這些消息并返回應答。
~~~
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
~~~
* 雙向流式 RPC,即兩邊都可以分別通過一個讀寫數據流來發送一系列消息。這兩個數據流操作是相互獨立的,所以客戶端和服務端能按其希望的任意順序讀寫,例如:服務端可以在寫應答前等待所有的客戶端消息,或者它可以先讀一個消息再寫一個消息,或者是讀寫相結合的其他方式。每個數據流里消息的順序會被保持。
~~~
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
~~~
我們將在下面 RPC 生命周期章節里看到各類 RPC 的技術細節。
### 使用 API 接口
gRPC 提供 protocol buffer 編譯插件,能夠從一個服務定義的 .proto 文件生成客戶端和服務端代碼。通常 gRPC 用戶可以在服務端實現這些API,并從客戶端調用它們。
* 在服務側,服務端實現服務接口,運行一個 gRPC 服務器來處理客戶端調用。gRPC 底層架構會解碼傳入的請求,執行服務方法,編碼服務應答。
* 在客戶側,客戶端有一個*存根*實現了服務端同樣的方法。客戶端可以在本地存根調用這些方法,用合適的 protocol buffer 消息類型封裝這些參數— gRPC 來負責發送請求給服務端并返回服務端 protocol buffer 響應。
### 同步 vs 異步
同步 RPC 調用一直會阻塞直到從服務端獲得一個應答,這與 RPC 希望的抽象最為接近。另一方面網絡內部是異步的,并且在許多場景下能夠在不阻塞當前線程的情況下啟動 RPC 是非常有用的。
在多數語言里,gRPC 編程接口同時支持同步和異步的特點。你可以從每個語言教程和參考文檔里找到更多內容(很快就會有完整文檔)。
## RPC 生命周期
現在讓我們來仔細了解一下當 gRPC 客戶端調用 gRPC 服務端的方法時到底發生了什么。我們不究其實現細節,關于實現細節的部分,你可以在我們的特定語言頁面里找到更為詳盡的內容。
### 單項 RPC
首先我們來了解一下最簡單的 RPC 形式:客戶端發出單個請求,獲得單個響應。
* 一旦客戶端通過樁調用一個方法,服務端會得到相關通知 ,通知包括客戶端的元數據,方法名,允許的響應期限(如果可以的話)
* 服務端既可以在任何響應之前直接發送回初始的元數據,也可以等待客戶端的請求信息,到底哪個先發生,取決于具體的應用。
* 一旦服務端獲得客戶端的請求信息,就會做所需的任何工作來創建或組裝對應的響應。如果成功的話,這個響應會和包含狀態碼以及可選的狀態信息等狀態明細及可選的追蹤信息返回給客戶端 。
* 假如狀態是 OK 的話,客戶端會得到應答,這將結束客戶端的調用。
### 服務端流式 RPC
服務端流式 RPC 除了在得到客戶端請求信息后發送回一個應答流之外,與我們的簡單例子一樣。在發送完所有應答后,服務端的狀態詳情(狀態碼和可選的狀態信息)和可選的跟蹤元數據被發送回客戶端,以此來完成服務端的工作。客戶端在接收到所有服務端的應答后也完成了工作。
### 客戶端流式 RPC
客戶端流式 RPC 也基本與我們的簡單例子一樣,區別在于客戶端通過發送一個請求流給服務端,取代了原先發送的單個請求。服務端通常(但并不必須)會在接收到客戶端所有的請求后發送回一個應答,其中附帶有它的狀態詳情和可選的跟蹤數據。
### 雙向流式 RPC
雙向流式 RPC ,調用由客戶端調用方法來初始化,而服務端則接收到客戶端的元數據,方法名和截止時間。服務端可以選擇發送回它的初始元數據或等待客戶端發送請求。 下一步怎樣發展取決于應用,因為客戶端和服務端能在任意順序上讀寫 - 這些流的操作是完全獨立的。例如服務端可以一直等直到它接收到所有客戶端的消息才寫應答,或者服務端和客戶端可以像"乒乓球"一樣:服務端后得到一個請求就回送一個應答,接著客戶端根據應答來發送另一個請求,以此類推。
### 截止時間
gRPC 允許客戶端在調用一個遠程方法前指定一個最后期限值。這個值指定了在客戶端可以等待服務端多長時間來應答,超過這個時間值 RPC 將結束并返回`DEADLINE_EXCEEDED`錯誤。在服務端可以查詢這個期限值來看是否一個特定的方法已經過期,或者還剩多長時間來完成這個方法。 各語言來指定一個截止時間的方式是不同的 - 比如在 Python 里一個截止時間值總是必須的,但并不是所有語言都有一個默認的截止時間。
### RPC 終止
在 gRPC 里,客戶端和服務端對調用成功的判斷是獨立的、本地的,他們的結論可能不一致。這意味著,比如你有一個 RPC 在服務端成功結束("我已經返回了所有應答!"),到那時在客戶端可能是失敗的("應答在最后期限后才來到!")。也可能在客戶端把所有請求發送完前,服務端卻判斷調用已經完成了。
### 取消 RPC
無論客戶端還是服務端均可以再任何時間取消一個 RPC 。一個取消會立即終止 RPC 這樣可以避免更多操作被執行。它*不是*一個"撤銷", 在取消前已經完成的不會被回滾。當然,通過同步調用的 RPC 不能被取消,因為直到 RPC 結束前,程序控制權還沒有交還給應用。
### 元數據集
元數據是一個特殊 RPC 調用對應的信息([授權詳情](http://doc.oschina.net/docs/guides/auth.html)\]) ,這些信息以鍵值對的形式存在,一般鍵的類型是字符串,值的類型一般也是字符串(當然也可以是二進制數據)。元數據對 gRPC 本事來說是不透明的 - 它讓客戶端提供調用相關的信息給服務端,反之亦然。 對于元數據的訪問是語言相關的。
### 頻道
在創建客戶端存根時,一個 gRPC 頻道提供一個特定主機和端口服務端的連接。客戶端可以通過指定頻道參數來修改 gRPC 的默認行為,比如打開關閉消息壓縮。一個頻道具有狀態,包含`已連接`和`空閑` 。 gRPC 如何處理關閉頻道是語言相關的。有些語言可允許詢問頻道狀態。