將 HTTP/JSON 轉碼為 gRPC

Cloud Endpoints 支援通訊協定轉碼,讓用戶端可使用 HTTP/JSON 存取您的 gRPC API。可擴充服務 Proxy (ESP) 能將 HTTP/JSON 轉碼為 gRPC。

本指南說明:

  • 如何在您的 .proto 檔案中使用註解,指定從 HTTP/JSON 到 gRPC 的資料轉換
  • 如何在 Endpoints 中部署您的服務以使用此功能
  • 哪裡可以找到有關設計和實作 gRPC 服務轉碼的更多參考資訊

本指南假設您已完成 gRPC 教學課程,且熟悉 gRPC 專用的 Endpoints API 基本概念。

設計容易轉碼的 API

轉碼包含將 HTTP/JSON 要求及其參數對應至 gRPC 方法,以及「這些方法的」參數和傳回類型。因此,雖然您可以將 HTTP/JSON 要求對應至任何任意 API 方法,但如果 gRPC API 的結構設計是以資源為導向 (就像傳統 HTTP REST API),就有助於進行此作業。換句話說,您設計 API 服務,讓服務使用少量與 HTTP 動詞 (例如 GETPUT) 相對應的標準方法,在服務的資源和資源集合上運作 (它們本身就是一種資源類型)。這些標準方法為 ListGetCreateUpdateDelete

如有必要,API 也可以有一些非標準的自訂方法,雖然這些方法不是很容易對應。

如要進一步瞭解資源導向設計和標準轉碼對應,請參閱 API 設計指南。設計指南是 Google 在設計 Cloud API 等公用 API 時所遵循的設計標準。雖然您不需要遵循此指南使用 gRPC 轉碼,仍強烈建議您這麼做。尤其是以下頁面可協助您瞭解這些設計原則,以及如何將有用的轉碼對應功能新增至您的方法:

以下參考頁面可能也很有用:

在本文件的其餘內容中,您將使用在教學課程中所使用的 Bookstore 範例,該範例已使用這些原則。Bookstore 有「書籍」資源的「書架」集合,可讓使用者 ListGetCreateDelete

哪裡可以設定轉碼功能

gRPC 轉碼功能預設為啟用,無需進行任何設定即可開始使用。請按照部署使用轉碼功能的服務的操作說明進行。之後,當您使用方法的要求訊息欄位值 (如果有的話) 做為 HTTP 要求主體中的 JSON,來將 HTTP POST 要求傳送至網址路徑 GRPC_SERVICE_FULL_NAME/METHOD_NAME>,ESP 會將要求訊息傳送至適當的 gRPC 方法。在上述範例中,GRPC_SERVICE_FULL_NAME 是 gRPC 服務的完整名稱,而 METHOD_NAME 是方法的名稱。

例如,如果您將 POST 傳送至 Bookstore 的 ListShelves 網址,如下所示:

curl -XPOST http://mydomain/endpoints.examples.bookstore.Bookstore/ListShelves

您會收到 JSON 格式的目前書架清單。

但是,就 HTTP 介面設計而言,我們強烈建議您如本文件的其餘部分中所述,明確地設定對應。

服務設定的 gRPC API 設定標準可讓您確切地指定資料應如何從 HTTP/JSON 轉譯至 gRPC。有兩種支援此操作的機制:在 .proto 檔案中直接註解,以及在 YAML 中直接註解 (做為 gRPC API 設定檔的一部分)。我們建議您使用 proto 註解以便讀取和維護。如要進一步瞭解 YAML 設定且可能需要使用這項功能,請參閱在 YAML 檔案中設定轉碼功能一節。

以下是從 Bookstore 使用建議方法的範例:

// Returns a specific bookstore shelf.
rpc GetShelf(GetShelfRequest) returns (Shelf) {
  // Client example - returns the first shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}" };
}

...
// Request message for GetShelf method.
message GetShelfRequest {
  // The ID of the shelf resource to retrieve.
  int64 shelf = 1;
}

註解會向 ESP 告知使用 http://mydomain/v1/shelves/1 網址提出 HTTP GET 要求會呼叫 gRPC 伺服器的 GetShelf() 方法,而 GetShelfRequest 包含要求的書架 ID shelf (在這個案例中為 1)。

新增轉碼對應功能

本節說明 Bookstore 範例中的一些其他對應註解。Bookstore 範例中有兩個 proto 檔案範例,以便使用或不使用轉碼對應功能進行部署,以及比較 proto 檔案中的差異:

要對如何指定轉碼對應有更全面的瞭解,請參閱標準方法自訂方法HTTP 規則參考資料

對應 List 方法

.proto 檔案中有 List 方法的定義及其回應類型:

  // Returns a list of all shelves in the bookstore.
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
    // Define HTTP mapping.
    // Client example (Assuming your service is hosted at the given 'DOMAIN_NAME'):
    //   curl http://DOMAIN_NAME/v1/shelves
    option (google.api.http) = { get: "/v1/shelves" };
  }
...
message ListShelvesResponse {
  // Shelves in the bookstore.
  repeated Shelf shelves = 1;
}

以粗體顯示的註解指定了此方法的 HTTP 對應。

  • option (google.api.http) 指定這個方法是 gRPC HTTP 對應註解。
  • get 指定這個方法已對應至 HTTP GET 要求。
  • "/v1/shelves"GET 要求用來呼叫這個方法的網址路徑範本 (已附加至服務的網域)。網址路徑也稱為資源路徑,因為它通常會指定您要使用的「物體」或資源。在這個案例中為 Bookstore 的所有書架資源。

舉例來說,如果用戶端將 GET 傳送至 http://mydomain/v1/shelves 網址以呼叫這個方法,ESP 就會呼叫 gRPC 方法ListShelves()。然後 gRPC 後端會傳回書架,而 ESP 則會將其轉換為 JSON 格式並傳回用戶端。

對應 Get 方法

.proto 檔案中定義了 Bookstore 的 GetShelf 方法及其要求和回應類型:

// Returns a specific bookstore shelf.
rpc GetShelf(GetShelfRequest) returns (Shelf) {
  // Client example - returns the first shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}" };
}

...
// Request message for GetShelf method.
message GetShelfRequest {
  // The ID of the shelf resource to retrieve.
  int64 shelf = 1;
}
...
// A shelf resource.
message Shelf {
  // A unique shelf id.
  int64 id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string theme = 2;
}

以粗體顯示的註解指定了此方法的 HTTP 對應。

  • option (google.api.http) 指定這個方法是 gRPC HTTP 對應註解。
  • get 指定這個方法已對應至 HTTP GET 要求。
  • 如前所述,"/v1/shelves/{shelf}" 是要求的網址路徑,但它指定了 /v1/shelves/{shelf}。這個大括號標記法會告知 ESP 位於 {shelf} 中的任何內容,就是它應在方法的 GetShelfRequest 參數中提供給 shelf 的值。

如果用戶端透過傳送 GET 至網址 http://mydomain/v1/shelves/4 來呼叫此方法,ESP 會以 shelf4 來建立 GetShelfRequest,然後使用它來呼叫 gRPC 方法 GetShelf()。接著 gRPC 後端會以 ID 4 傳回要求的 Shelf,然後 ESP 會將其轉換為 JSON 格式並傳回用戶端。

使用這個方法時,shelf 用戶端只需要提供單一要求欄位值,即您在網址路徑範本中使用大括號「擷取」標記法指定的值。如有必要,您也可以擷取網址的多個部分以識別要求的資源。例如,GetBook 方法需要用戶端在網址中同時指定書架 ID 和書籍 ID:

// Returns a specific book.
rpc GetBook(GetBookRequest) returns (Book) {
  // Client example - get the first book from the second shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/2/books/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}/books/{book}" };
}
...
// Request message for GetBook method.
message GetBookRequest {
  // The ID of the shelf from which to retrieve a book.
  int64 shelf = 1;
  // The ID of the book to retrieve.
  int64 book = 2;
}

除了欄位值的文字和擷取大括號以外,網址路徑範本還可以使用萬用字元,來表示網址這部分中的任何內容都應擷取。上述範例使用的 {shelf} 標記法其實是 {shelf=*} 的捷徑。您可在 HTTP 規則參考一文中進一步瞭解路徑範本的規則。

此方法類型沒有指定的 HTTP 要求內容:如要進一步瞭解如何對應 Get 方法 (包括使用查詢參數),請參閱標準方法一文。

對應 Create 方法

Bookstore 的 CreateShelf 方法會對應至 HTTP POST

  // Creates a new shelf in the bookstore.
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
    // Client example:
    //   curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves
    option (google.api.http) = {
      post: "/v1/shelves"
      body: "shelf"
    };
  }
...
// Request message for CreateShelf method.
message CreateShelfRequest {
  // The shelf resource to create.
  Shelf shelf = 1;
}
...
// A shelf resource.
message Shelf {
  // A unique shelf id.
  int64 id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string theme = 2;
}
  • option (google.api.http) 指定這個方法是 gRPC HTTP 對應註解。
  • post 指定這個方法已對應至 HTTP POST 要求。
  • 如同之前一樣,"/v1/shelves" 是要求的網址路徑。
  • HTTP 要求主體中會使用 body: "shelf",以 JSON 格式指定您要新增的資源。

因此,如果用戶端像這樣呼叫此方法:

curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves

ESP 會使用 JSON 主體建立具有 CreateShelfRequest 適用的 "Music" 主題的 Shelf 值,然後呼叫 gRPC CreateShelf() 方法。請注意,用戶端「不會」為 Shelf 提供 id。建立新書架時,服務會提供 Bookstore 的書架 ID。 您在 API 說明文件中提供這種類型的資訊給服務的使用者。

在內容中使用萬用字元

您可以在主體對應中使用特殊名稱 *,將每個未與路徑範本繫結的欄位定義為應該對應至要求主體。如此一來,即會啟用下列 CreateShelf 方法的替代定義。

  // Creates a new shelf in the bookstore.
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
    // Client example:
    //   curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123
    option (google.api.http) = {
      post: "/v1/shelves/{shelf_id}"
      body: "*"
    };
  }
...
// Request message for CreateShelf method.
message CreateShelfRequest {
  // A unique shelf id.
  int64 shelf_id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string shelf_theme = 2;
  // The size of the shelf
  int64 shelf_size = 3;
}
  • option (google.api.http) 指定這個方法是 gRPC HTTP 對應註解。
  • post 指定這個方法已對應至 HTTP POST 要求。
  • "/v1/shelves/{shelf_id}" 是要求的網址路徑。{shelf_id} 中的任何內容都是 CreateShelfRequestshelf_id 欄位的值。
  • 在本例中,body: "*" 會用於 HTTP 要求主體,指定 shelf_id 以外的所有要求欄位,包括 shelf_themeshelf_size。如果 JSON 主體中含有這兩個名稱的任何欄位,其值會用於 CreateShelfRequest 的對應欄位。

舉例來說,如果用戶端像這樣呼叫此方法:

curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123

ESP 會使用 JSON 主體和路徑範本建立 CreateShelfRequest{shelf_id: 123 shelf_theme: "Music" shelf_size: 20},然後使用該 CreateShelfRequest{shelf_id: 123 shelf_theme: "Music" shelf_size: 20} 呼叫 gRPC CreateShelf() 方法。詳情請參閱 HttpRule

在 YAML 檔案中設定轉碼功能

另一種方法是您在 gRPC API 設定 YAML 檔案 (而不是 .proto 檔案) 中指定 HTTP 至 gRPC 對應。如果您有用於多個服務的單一 proto API 定義,並為每個服務指定不同的對應,您可能需要在 YAML 檔案中設定轉碼。

YAML 檔案的 http 區段中的 rules 指定如何將 HTTP/JSON 要求對應至 gRPC 方法:

http:
  rules:
  ...
  #
  # 'GetShelf' is available via the GET HTTP verb and '/shelves/{shelf}' URL
  # path, where {shelf} is the value of the 'shelf' field of 'GetShelfRequest'
  # protobuf message.
  #
  # Client example - returns the first shelf:
  #   curl http://DOMAIN_NAME/v1/shelves/1
  #
  - selector: endpoints.examples.bookstore.Bookstore.GetShelf
    get: /v1/shelves/{shelf}
  ...

api_config_http.yaml 中有更完整的範例,說明如何將這個方法用於 Bookstore 服務範例。

部署使用轉碼的服務

部署使用轉碼功能的 gRPC 服務與部署任何其他 gRPC 服務大致相同,但有一個主要區別。在教學課程中,範例需要接受來自用戶端範例的 gRPC 要求。不過,如果您也想要讓 Bookstore 接受 HTTP 要求,則須針對 ESP 執行一些額外的設定。用戶端使用 HTTP1.1 通訊協定將 JSON/HTTP 要求傳送至 ESP,因此 ESP 必須設定為使用 SSL (SSL 通訊埠可同時支援兩種要求類型),或必須啟用特殊通訊埠才能接受這些呼叫。剩下的部署作業與您所選環境的教學課程大致相同。

確保已部署 HTTP 規則

如果您已下載 Bookstore 範例以進行我們的教學課程,請注意,您必須下載稍微不同版本的 .proto 檔案,且該版本中包含註解:http_bookstore.proto。執行 protoc 之前,您也需要從 GitHub 複製 googleapis 存放區,因為您在包含的路徑中需要有 annotations.proto

    git clone https://github.com/googleapis/googleapis

    GOOGLEAPIS_DIR=<your-local-googleapis-folder>

接著,將設定部署至 Endpoints 時,您需要從 http_bookstore.proto 建立新的 .pb 描述元:

    protoc \
        --include_imports \
        --include_source_info \
        --proto_path=${GOOGLEAPIS_DIR} \
        --proto_path=. \
        --descriptor_set_out=api_descriptor.pb \
        http_bookstore.proto

如果您使用替代方法在 gRPC API 設定 YAML 檔案中設定 HTTP 對應,則還得確保您在將設定部署至 Endpoints 前已先部署相關規則。如要使用 Bookstore 服務嘗試此操作,其基本規則位於 api_config.yaml 檔案中,HTTP 規則則位於 api_config_http.yaml 檔案中:

    gcloud endpoints services deploy api_descriptor.pb api_config.yaml api_config_http.yaml

使用 SSL

如果您在用戶端和 ESP 之間的通訊啟用了安全資料傳輸層 (SSL),用戶端即可以使用同一個通訊埠來進行 gRPC 或 HTTP1.1 呼叫。要瞭解如何為 Endpoints 服務設定 SSL,請參閱啟用 SSL 相關頁面。

在 Google Kubernetes Engine 設定檔 (GKE) 中使用 --ssl_port 參數或使用 docker run 指令 (Compute Engine/Docker),指定通訊埠讓 ESP 能夠接受 SSL 呼叫。

    args: [
      "--http_port", "8080",
      "--ssl_port", "443",  # enable SSL port at 443 to serve https requests
      "--backend",  "grpc://127.0.0.1:8081",  # gRPC backend.
      "--service", "SERVICE_NAME",
      "--rollout_strategy", "managed",
    ]

設定 HTTP1.1 通訊埠

如果您不是使用 SSL,則須為 HTTP1.1 要求設定個別的通訊埠,因為 gRPC 和 HTTP1.1 無法在沒有 SSL 的情況下共用相同的通訊埠。在 GKE 設定檔中使用 --http_port 參數或使用 docker run 指令,指定要接受 HTTP1.1 呼叫的通訊埠。如果您也想要讓 ESP 接受 gRPC 呼叫,則也需要使用 --http2_port 參數來指定 gRPC 通訊埠。

    args: [
      "--http_port", "8080",  # for HTTP 1.1
      "--http2_port", "8090",  # for gRPC
      "--backend", "grpc://127.0.0.1:8081",  # gRPC backend.
      "--service", "SERVICE_NAME",
      "--rollout_strategy", "managed",
    ]

使用轉碼功能呼叫服務

本節將說明服務設定以及如何對服務執行 HTTP 呼叫。

服務設定

本節假設您已針對您選擇的環境完成基本 gRPC 服務教學課程,且有 GKE 叢集或 Compute Engine 執行個體來執行範例。

  1. 首先,如上方確保已部署 HTTP 規則一節中所述,確保您已將支援 HTTP 的 Bookstore 服務設定部署至 Endpoints。
  2. 依照所選平台的教學課程所說明的操作步驟來部署後端和 ESP,並使用 --http_port 參數為 HTTP1.1 要求啟用通訊埠:

對服務進行 HTTP 呼叫

  1. 取得 ESP 的外部 IP 位址,並將其設定為 $ESP_IP
  2. 使用 curl 發出以下 HTTP 要求

    curl http://$ESP_IP/v1/shelves
    

    (如果您使用的是安全資料傳輸層 (SSL),則使用與 https:// 相同的網址)。伺服器會回應:

    {"shelves":[{"id":"1","theme":"Fiction"},{"id":"2","theme":"Fantasy"}]}
    

    如果輸出顯示二進位的回應,請檢查您的通訊埠設定,因為您可能正在使用 HTTP2 通訊埠,而非 HTTP 通訊埠。

  3. 試用 Create 方法。CreateShelf 需要 API 金鑰,因此您需要為專案建立金鑰,並將其設為 $KEY。現在,請呼叫:

    curl -d '{"theme":"Music"}' http://$ESP_IP/v1/shelves?key=$KEY
    

    如果您再次呼叫 GetShelves,應該會看到新的書架。