Cloud Endpoints 支持协议转码,以便客户端可以使用 HTTP/JSON 访问您的 gRPC API。Extensible Service Proxy (ESP) 可将 HTTP/JSON 转码为 gRPC。
本指南介绍了以下内容:
- 如何使用
.proto
文件中的注释来指定从 HTTP/JSON 到 gRPC 的数据转换 - 如何在 Endpoints 中部署您的服务以使用此功能
- 在哪里可以找到有关为 gRPC 服务设计和实现转码的更多参考信息
假设您已完成我们的 gRPC 教程并熟悉适用于 gRPC API 的 Endpoints 基本概念。
设计易于转码的 API
转码涉及将 HTTP/JSON 请求及其参数映射到 gRPC 方法及其参数和返回类型。因此,尽管可以将 HTTP/JSON 请求映射到任意 API 方法,但如果您以面向资源的方式设计 gRPC API 的结构(就像传统 HTTP REST API 一样),则有助于实现映射。换句话说,您可设计 API 服务,让其使用少量标准方法,并与操作该服务的资源和资源集合(本身是一种资源类型)的 GET
和 PUT
等 HTTP 谓词相对应。这些标准方法包括 List
、Get
、Create
、Update
和 Delete
。
如有必要,API 还可以采用一些非标准的自定义方法,不过这些方法的映射并不简单。
您可以在 API 设计指南中更详细地了解面向资源的设计和标准转码映射。该设计指南是 Google 在设计 Cloud API 等公共 API 时遵循的设计标准。虽然您无需遵循该指南来使用 gRPC 转码,但我们强烈建议您使用该转码。具体来说,以下页面将帮助您了解这些设计原则,并在您的方法中添加有用的转码映射:
以下参考页面可能也有用:
- Http 规则参考:关于 HTTP 映射规则的全面参考
在本文档的其余部分,您会用到在我们的教程中使用的 Bookstore 示例,该示例已采用上述原则。Bookstore 具有“图书”资源的“书架”集合,用户可以执行 List
、Get
、Create
或 Delete
方法。
何处配置转码
gRPC 转码默认启用,您可在不进行任何配置的情况下使用它。按照相关说明来部署使用转码的服务。之后,当您以 JSON 格式在 HTTP 请求正文中通过方法的请求消息字段值(如有)向网址路径 GRPC_SERVICE_FULL_NAME/METHOD_NAME>
发送 HTTP POST
请求时,ESP 会将请求消息发送给相应的 gRPC 方法。在上述示例中,GRPC_SERVICE_FULL_NAME
是 gRPC 服务的全名,METHOD_NAME
是该方法的名称。
例如,如果您向 Bookstore 的 ListShelves
网址发送 POST
(如下所示):
curl -XPOST http://mydomain/endpoints.examples.bookstore.Bookstore/ListShelves
则您会获得 JSON 形式的最新书架列表。
不过,在 HTTP 接口设计方面,强烈建议您按照本文档其余部分中的说明,明确配置映射。
通过针对服务配置的 gRPC API 配置标准,您可以准确指定数据应如何从 HTTP/JSON 转换为 gRPC。为此,您可以使用两种机制:使用 .proto
文件中的直接注释,以及在属于 gRPC API 配置文件的 YAML 中配置。建议您使用 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()
方法,使用包含所请求的书架 ID shelf
(在此示例中为 1
)的 GetShelfRequest
。
添加转码映射
本部分介绍 Bookstore 示例中的其他一些映射注释。Bookstore 示例中有两个示例 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
指定此方法映射到 HTTPGET
请求。"/v1/shelves"
是GET
请求在调用该方法时使用的网址路径模板(附加到服务的网域)。网址路径也称为资源路径,因为它通常用于指定您要使用的“对象”或资源。在本例中是指我们的 Bookstore 的所有书架资源。
例如,如果客户端通过向网址 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
指定此方法映射到 HTTPGET
请求。- 如前所述,
"/v1/shelves/{shelf}"
是请求的网址路径,但它先指定/v1/shelves/
,然后指定{shelf}
。该花括号表示法告知 ESP,{shelf}
中的任何内容都是 ESP 应在此方法的GetShelfRequest
参数中为shelf
提供的值。
如果客户端通过向网址 http://mydomain/v1/shelves/4
发送 GET
来调用此方法,则 ESP 会创建一个 shelf
值为 4
的 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
指定此方法映射到 HTTPPOST
请求。- 如前所述,
"/v1/shelves"
是请求的网址路径。 body: "shelf"
在 HTTP 请求正文中用来以 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
指定此方法映射到 HTTPPOST
请求。"/v1/shelves/{shelf_id}"
是该请求的网址路径。{shelf_id}
中的内容就是shelf_id
字段在CreateShelfRequest
中的值。body: "*"
在 HTTP 请求正文中用于指定此示例中除shelf_id
之外的所有剩余请求字段,这些字段是shelf_theme
和shelf_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}
,然后使用该模板调用 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
协议向 ESP 发送 JSON/HTTP 请求,因此 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
文件定义了其基本规则,api_config_http.yaml
文件定义了其 HTTP 规则:
gcloud endpoints services deploy api_descriptor.pb api_config.yaml api_config_http.yaml
使用 SSL
如果为客户端与 ESP 之间的通信启用了 SSL,则客户端可以使用同一端口进行 gRPC 或 HTTP1.1
调用。您可以在启用 SSL 中了解如何为 Endpoints 服务设置 SSL。
在 Google Kubernetes Engine 配置文件 (GKE) 或 docker run
命令 (Compute Engine/Docker) 中使用 --ssl_port
标志来为 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
请求设置一个单独的端口,这是因为在不使用 SSL 的情况下 gRPC 和 HTTP1.1
不能共用同一端口。在 GKE 配置文件或 docker run
命令中使用 --http_port
标志来指定一个端口,用于接受 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://
的相同网址。)服务器会做出如下响应:{"shelves":[{"id":"1","theme":"Fiction"},{"id":"2","theme":"Fantasy"}]}
如果输出显示二进制响应,请检查端口配置,因为您正在使用的可能是 HTTP2 端口,而不是 HTTP 端口。
尝试使用
Create
方法。CreateShelf
需要 API 密钥,因此您需要为项目创建一个密钥并将其设置为$KEY
。现在,请调用:curl -d '{"theme":"Music"}' http://$ESP_IP/v1/shelves?key=$KEY
如果您再次调用
GetShelves
,应该会看到新书架。