Cloud Endpoints はプロトコルのコード変換をサポートしており、クライアントは HTTP / JSON を使用して gRPC API にアクセスできます。HTTP / JSON から gRPC へのコード変換は Extensible Service Proxy(ESP)が行います。
このガイドでは、次の内容を説明します。
.proto
ファイルでアノテーションを使用して、HTTP / JSON から gRPC へのデータ変換を指定する方法- この機能を使用するために Endpoints にサービスをデプロイする方法
- gRPC サービス用のコード変換の設計と実装に関するリファレンス情報が見つかる場所
このドキュメントを読む前提条件として、gRPC のチュートリアルをすでに完了し、Cloud Endpoints for gRPC の API の基本コンセプトを理解している必要があります。
コード変換に適した API の設計
コード変換では、HTTP/JSON リクエストとそのパラメータを、gRPC のメソッド、そのパラメータ、戻り値の型にマッピングします。そのため、HTTP / JSON リクエストを任意の API メソッドにマッピングできます。特に、gRPC API が従来の HTTP REST API と同じようにリソース指向の方法で構造化されている場合には、この方法が役立ちます。言い換えると、サービスのリソースおよび、それ自体が一種のリソースである、リソースのコレクションに対して動作する少数の標準メソッド(GET
や PUT
のような HTTP 動詞に対応するメソッド)を使用するように、API サービスを設計する必要があります。このような標準メソッドは、List
、Get
、Create
、Update
、Delete
です。
必要な場合は、標準ではないカスタム メソッドを使用することもできますが、マッピングは簡単ではありません。
リソース指向の設計と標準コード変換マッピングの詳細については、API 設計ガイドをご覧ください。このデザインガイドは、Cloud APIs のような公開 API を設計するときに Google が従う設計標準です。このガイドに従わなくても gRPC コード変換を使用することはできますが、ガイドに準拠することを強くおすすめします。これらの設計原理を理解し、役に立つコード変換マッピングをメソッドに追加するには、以下のページが特に参考になります。
以下の参照ページも役に立ちます。
- HTTP ルール リファレンス: HTTP マッピング ルールに関する全体的なリファレンス
以降では、チュートリアルで使用した Bookstore の例を使用します。この例では、以上の原理がすでに使用されています。Bookstore には「book」リソースの「shelf」コレクションがあり、ユーザーはこれをリスト表示(List
)、取得(Get
)、作成(Create
)、または削除(Delete
)できます。
コード変換を構成する場所
gRPC コード変換がデフォルトで有効になり、そのままの構成でこれを使用できます。コード変換を使用するサービスのデプロイの手順に沿ってください。その後、メソッドのリクエスト メッセージ フィールドの値(ある場合)を HTTP リクエスト本文の JSON として使用し、HTTP POST
リクエストを URL パス GRPC_SERVICE_FULL_NAME/METHOD_NAME>
に送信すると、ESP はリクエスト メッセージを適切な gRPC メソッドに送信します。上記の例では、GRPC_SERVICE_FULL_NAME
は gRPC サービスのフルネーム、METHOD_NAME
はメソッドの名前です。
たとえば、次のようにして、POST
を Bookstore の ListShelves
URL に送信します。
curl -XPOST http://mydomain/endpoints.examples.bookstore.Bookstore/ListShelves
この場合は、書棚の現在のリストが JSON フォームで返されます。
ただし、後で説明するように、HTTP インターフェースの設計ではマッピングを明示的に構成することを強くおすすめします。
サービス構成の gRPC API 構成標準では、HTTP / JSON から gRPC へのデータの変換方法を厳密に指定できます。そのために、.proto
ファイルでの直接アノテーション、および gRPC API 構成ファイルの一部としての YAML という 2 つのメカニズムがサポートされています。読みやすさと保守の容易さの点から、proto
アノテーションを使用することをおすすめします。YAML の構成の詳細と、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;
}
このアノテーションでは、URL http://mydomain/v1/shelves/1
で HTTP GET
リクエストが行われると、gRPC サーバーの GetShelf()
メソッドを、要求された書棚 ID shelf
(この場合は 1
)を含む GetShelfRequest
を使って呼び出すように ESP に指示しています。
コード変換マッピングの追加
このセクションでは、Bookstore サンプルのその他のマッピング アノテーションについて説明します。Bookstore サンプルには 2 つのサンプル proto
ファイルがあるので、コード変換マッピングの有無にかかわらずデプロイでき、proto
ファイルの違いを比較できます。
bookstore.proto
: Endpoints チュートリアルで使用され、コード変換マッピングがありません。http_bookstore.proto
: コード変換バインディングが追加されています。
コード変換マッピングの指定に関するさらに詳細なガイドについては、標準メソッド、カスタム メソッド、HTTP ルール リファレンスをご覧ください。
List
メソッドをマッピングする
List
メソッドは、そのレスポンスのデータ型とともに .proto
ファイルに定義されています。
// 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
リクエストが使用する(サービスのドメインに追加される)URL パス テンプレートです。URL パスは使用する「もの」つまりリソースを通常指定するので、リソースパスとも呼ばれます。この場合は Bookstore のすべての書棚リソースです。
たとえば、クライアントが URL http://mydomain/v1/shelves
に GET
を送信してこのメソッドを呼び出すと、ESP は gRPC メソッド ListShelves()
を呼び出します。その後、gRPC バックエンドは書棚を返し、ESP はそれを JSON 形式に変換してクライアントに返します。
Get
メソッドをマッピングする
Bookstore の GetShelf
メソッドは、そのリクエストとレスポンスのデータ型とともに .proto
ファイルに定義されています。
// 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}"
は、前と同じようにリクエストへの URL パスですが、/v1/shelves/
を指定してから{shelf}
を指定します。この中括弧の表記は、{shelf}
内の値をメソッドのGetShelfRequest
パラメータでshelf
に提供する必要があることを ESP に伝えます。
クライアントが GET
を URL http://mydomain/v1/shelves/4
に送信することによってこのメソッドを呼び出した場合、ESP は 4
の値を shelf
にして GetShelfRequest
を作成した後、それを使って gRPC メソッド GetShelf()
を呼び出します。gRPC バックエンドは要求された ID 4
の Shelf
を返し、ESP はそれを JSON 形式に変換してクライアントに返します。
このメソッドの場合、クライアントが提供する必要のあるリクエスト フィールド値は shelf
だけであり、これは URL パス テンプレートで中括弧の「キャプチャ」表記を使って指定されています。必要な場合は、要求されたリソースを識別するために URL の複数の部分をキャプチャすることもできます。たとえば、GetBook
メソッドでは、クライアントは書棚 ID と書籍 ID を URL で指定する必要があります。
// 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;
}
URL パス テンプレートでは、フィールド値のリテラルとキャプチャ括弧以外にも、ワイルドカードを使用して、URL のこの部分にあるものをすべてキャプチャすべきであることを示すことができます。上記の例で使用されている {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"
は、前と同じようにリクエストの URL パスです。body: "shelf"
は、追加するリソースを JSON 形式で指定するために、HTTP リクエスト本文で使われます。
たとえば、クライアントが次のようにこのメソッドを呼び出すものとします。
curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves
ESP は JSON 本文を使用して、テーマ "Music"
で CreateShelfRequest
の 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}"
は、リクエストの URL パスです。{shelf_id}
にどんな値が入っていても、CreateShelfRequest
のshelf_id
フィールドの値です。- HTTP リクエストの本文で
body: "*"
を使用すれば、以下の例のようにshelf_id
を除くすべての残りのリクエスト フィールド、shelf_theme
とshelf_size
を指定できます。これら 2 つの名前を持つ 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}
を作成し、それを使用して gRPC の CreateShelf()
メソッドを呼び出します。詳細については、HttpRule をご覧ください。
YAML でのコード変換の構成
HTTP から gRPC へのマッピングを、.proto
ファイルの代わりに gRPC API 構成 YAML ファイルで指定する方法もあります。1 つの 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}
...
Bookstore サービスでこのアプローチを使用している詳細な例については、api_config_http.yaml
をご覧ください。
コード変換を使用するサービスのデプロイ
コード変換を使用する gRPC サービスのデプロイは、他の gRPC サービスのデプロイとほとんど同じですが、1 つだけ大きな違いがあります。チュートリアルの例では、サンプル クライアントから gRPC リクエストを受け取ることが必要でした。一方で、Bookstore が HTTP リクエストも受け取るようにする場合は、ESP に追加の構成が必要です。クライアントは HTTP1.1
プロトコルを使用して JSON/HTTP リクエストを ESP に送信するため、ESP は SSL を使用するように構成する(SSL ポートは両方のリクエスト タイプをサポート可能)か、呼び出しを受け取るための専用のポートを有効にする必要があります。デプロイメントのそれ以外の部分は、選択した環境のチュートリアルとほぼ同じです。
デプロイされた HTTP ルールの確認
チュートリアルの Bookstore サンプルをすでにダウンロードしている場合は、アノテーションを含むわずかに異なるバージョンの .proto
ファイル(http_bookstore.proto
)をダウンロードする必要があります。また、インクルード パスで annotations.proto
が必要になるため、protoc
を実行する前に GitHub から googleapis
リポジトリのクローンを作成する必要もあります。
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 を使用しない場合、gRPC と HTTP1.1
は SSL なしで同じポートを共有できないため、HTTP1.1
リクエスト用に別のポートを設定する必要があります。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 インスタンスがあることを前提としています。
- 最初に、HTTP が有効な Bookstore サービスの構成を Endpoints にデプロイしてあることを確認します(デプロイされた HTTP ルールの確認をご覧ください)。
選択したプラットフォームに対応するチュートリアルの説明に従って、バックエンドと ESP をデプロイします。このとき、
--http_port
フラグを使用してHTTP1.1
リクエスト用のポートを有効にします。- GKE のデプロイメント: クラスタにサンプル API と ESP をデプロイするの説明に従います。デプロイする際は、GKE 構成ファイルで
"--http_port"
フラグが指定されていることを確認します。 - Compute Engine のデプロイメント: Docker コンテナでサンプル API と ESP を実行するの説明に従います。
パッケージ済みの ESP Docker コンテナを実行するときは、
docker run
コマンドで--http_port
フラグが指定されていることを確認します。
- GKE のデプロイメント: クラスタにサンプル API と ESP をデプロイするの説明に従います。デプロイする際は、GKE 構成ファイルで
サービスに対する HTTP 呼び出しの実行
- ESP の外部 IP アドレスを取得し、それを
$ESP_IP
に設定します。 curl
で次の HTTP リクエストを行いますcurl http://$ESP_IP/v1/shelves
(SSL を使用している場合は
https://
が付いた同じ URL を使用します)。サーバーから次のレスポンスがあります。{"shelves":[{"id":"1","theme":"Fiction"},{"id":"2","theme":"Fantasy"}]}
出力にバイナリ レスポンスが表示される場合は、ポートの構成を確認します。HTTP ポートではなく HTTP2 ポートにアクセスしている可能性があります。
Create
メソッドを呼び出してみます。CreateShelf
には API キーが必要なため、プロジェクトのキーを作成し、それを$KEY
として設定します。次の呼び出しを行います。curl -d '{"theme":"Music"}' http://$ESP_IP/v1/shelves?key=$KEY
GetShelves
を再び呼び出すと、新しい書棚が表示されます。