[譯] 爲何咱們要切換到 gRPC

若是你在使用微服務式架構,那麼你須要做出的一個基本決策就是:服務之間應該如何交換信息?默認的方法彷佛是使用 HTTP 協議發送 JSON 信息 —— 也就是使用所謂的 REST API,可是大多數人並無認真執行 REST 的原則。使用 REST API 的 fromAtoB 就是咱們最開始使用的方法,然而最近咱們決定將 gRPC 做爲咱們新的標準。前端

gRPC 是谷歌研發,而且已經開源的遠程過程調用系統。雖然它已經存在數年之久,但我並無在網上找到關於人們爲何使用或者不使用它的信息,因此我決定寫一篇文章,闡述咱們選擇了 gRPC 的緣由。android

gRPC 最大的優點就是它使用的是高效二進制編碼,這讓它比 JSON/HTTP 這種模式快了不少。雖然更快的速度每每是很受歡迎的,這裏還有兩個對於咱們而言更重要的方面:清晰的接口規範,以及對流的支持。ios

gRPC 接口規範

當你建立要新的 gRPC 服務時,第一步一般是在 .proto 文件中定義接口。下面這段代碼就是 .proto 文件大體的格式 —— 它是咱們本身的 API 中一小部分的簡化版。在這個例子中,定義了一個遠程過程調用「Lookup」,以及它的輸入和輸出類型。git

syntax = "proto3";

package fromatob;

// FromAtoB 是 fromAtoB 後臺 API 的簡化版本。
service FromAtoB {
	rpc Lookup(LookupRequest) returns (Coordinate) {}
}

// LookupRequest 是按照名稱查找城市座標的請求
message LookupRequest {
	string name = 1;
}

// Coordinate 使用經度和緯度定義了地球上的座標
message Coordinate {
  // Latitude 是座標的緯度,範圍是 [-90, 90]。
	double latitude = 1;

  // Longitude 是座標的經度,範圍是 [-180, 180]。
	double longitude = 2;
}
複製代碼

使用了這個文件,接下來你就可使用 protoc 編譯器生成客戶端和服務端代碼,同時你也能夠開始編寫提供或者使用 API 的代碼了。github

因此,爲何這個文件能爲咱們帶來優點,而不是冗餘的工做呢?讓咱們再看一遍上面的代碼樣例。就算你歷來沒有用過 gRPC 或者協議緩衝(Protocol Buffer),這段代碼也很是易讀:例如,很容易看出,若是想要發出 Lookup 請求,你必須發送一個 string 類型的 name 參數,這個請求將會返回給你一個 Coordinate 類型的結果,它包含了參數 latitudelongitude。事實上,一旦你像例子中的那樣添加了一些簡單的註釋,.proto 文件就能夠做爲你的服務的 API 文檔了。json

固然,一個真正的服務規範的內容應該要多得多,可是卻不會更加複雜。只是會有更多對於方法的 rpc 聲明和對於類型的 message 聲明。後端

經過 protoc 生成的代碼也會確保客戶端或者服務端發送的數據都合乎規範。這對於調試是大有幫助的。我記得以前就曾有過兩次,我負責維護的服務生成了錯誤格式的 JSON 數據,而且因爲這個格式並無被驗證,錯誤僅會在用戶界面出現。發現錯誤的惟一方法就是調試前端的 JavaScript 代碼 —— 這對於一個歷來沒有使用過前端 JavaScript 框架的後端開發者並不容易!api

Swagger / OpenAPI

原則上來講,若是你同時使用了 HTTP/JSON API 和 Swagger 或者它的繼承者 OpenAPI,你也能夠得到一樣的優點。下面這段代碼範例也能夠和 gRPC API 媲美:數組

openapi: 3.0.0

info:
  title: A simplified version of fromAtoB’s backend API
  version: '1.0'

paths:
  /lookup:
    get:
      description: Look up the coordinates for a city by name.
      parameters:
        - in: query
          name: name
          schema:
            type: string
          description: City name.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Coordinate'
        '404':
          description: Not Found
          content:
            text/plain:
              schema:
                type: string

components:
  schemas:
    Coordinate:
      type: object
      description: A Coordinate identifies a location on Earth by latitude and longitude.
      properties:
        latitude:
          type: number
          description: Latitude is the degrees latitude of the location, in the range [-90, 90].
        longitude:
          type: number
          description: Longitude is the degrees longitude of the location, in the range [-180, 180].
複製代碼

將這段代碼和 gRPC 規範相比。OpenAPI 的代碼就顯得很是難以讀懂!它更加冗長,結構也更復雜(有八級縮進,不像 gRPC 的只有一級)。緩存

使用 OpenAPI 規範進行驗證也要比 gRPC 難不少。至少對於內部服務,這一切都意味着規範要麼沒有被寫入,要麼隨着 API 的迭代,它們因爲沒有更新而變得無用了。

今年更早些的時候,我開始爲咱們的搜索引擎設計新的 API(想象一下我要搜索「請給我全部 2019 年 6 月 1 日從柏林到巴黎的航線」)。在我構建了初版使用 HTTP 和 JSON 的 API 以後,個人一個同事指出,在某些狀況下,我須要流式的請求結果,意味着我獲取到第一個請求結果的時候,我應該開始再發送這些結果。而我設計的 API 只是返回一個簡單的 JSON 數組,因此服務在獲取到全部結果以前,沒法發送任何請求。

前端使用這樣的 API 就須要客戶端發起輪詢請求結果。前端發起 POST 請求來設置搜索條件,而後反覆發送 GET 請求來獲取結果。返回結果會包含一個能夠確認搜索是否已經完成的字段。這種方式能夠正常運行,可是不夠優雅,而且還須要服務端使用像 Redis 這樣的數據存儲來緩存中間結果。新的 API 可能會被大量的更小的服務來實現,我並不但願強制它們都實現這樣的邏輯。

因而,咱們就決定要試一試採用 gRPC。若是你想要發送遠程調用的結果,使用了 gRPC,你只須要將 stream 關鍵字添加到 .proto 文件中。這就是 Search 函數的定義:

rpc Search (SearchRequest) returns (stream Trip) {}
複製代碼

protoc 編譯器生成的代碼包含了一個帶有 Send 函數的對象,咱們的服務代碼將會調用這個函數,來一個接一個的發送 Trip 對象,還會包含一個帶有 Recv 函數的對象,而客戶端代碼將會調用這個函數來獲取結果。從一個開發者的角度來看,這比應用輪詢要簡單不少

注意事項

另外我還想提一下,gRPC 也有些缺點。它們都與工具備關,而不是協議自己的問題。

當你使用 HTTP/JSON 構建 API 的時候,你可使用 curl、httpie 或 Postman 來作簡單的測試。對於 gRPC 也有相似的工具,即 grpcurl,可是它和 gRPC 並非那麼無縫銜接的:你必須在服務端添加 gRPC 服務映射擴展,或者在每一個命令中指定對應的 .proto 文件。咱們認爲在服務端添加一個小小的能夠發送簡單請求的命令行工具更爲簡便。而 protoc 生成的客戶端代碼已經讓發送請求很是簡單了。

另外一個更大的問題則是 Kubernete 的負載均衡,咱們曾經用於 HTTP 服務的負載均衡並不很是適用於 gRPC。基本上來講,gRPC 須要的負載均衡是在應用層面而不是 TCP 鏈接的層面。爲了解決這個問題,咱們參考教程:gRPC Load Balancing on Kubernetes without Tears,建立了 Linkerd

總結

儘管構建 gRPC API 會須要更多的前期工做,然而所以可以擁有清晰的 API 規範以及對流的支持,咱們發現這些獲益徹底可以彌補這些前期的工做量。對於咱們而言,gRPC 將會是全部咱們將構建的新的內部服務的默認選擇。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索