RSS

结合 REST 和 Open API 的 gRPC

今天的客座文章来自 CoreOS 的 Brandon Phillips。CoreOS 为 Linux 容器构建开源项目和产品。他们用于共识和发现的旗舰产品 etcd 以及容器引擎 rkt 都是 gRPC 的早期采用者。

CoreOS 选择 gRPC 的一个关键原因是它使用了 HTTP/2,这使得应用程序能够在单个 TCP 端口上同时提供 HTTP 1.1 REST/JSON API 和高效的 gRPC 接口(Go 语言可用)。这为开发者提供了与 REST Web 生态系统的兼容性,同时推进了一种新的、高效的 RPC 协议。随着 Go 1.6 的发布,Go 默认提供了稳定的 net/http2 包。

由于许多 CoreOS 客户端使用 HTTP 1.1 和 JSON 进行通信,gRPC 与 JSON 以及 开放 API 规范(Open API Specification,前身为 Swagger)的轻松互操作性非常宝贵。对于那些更习惯于基于 HTTP/1.1+JSON 和开放 API 规范的 API 的用户,他们使用了一系列开源库,通过 API 多路复用器使 gRPC 服务同时提供 gRPC 和 HTTP REST 两种形式,从而为用户提供两全其美的体验。让我们深入了解细节,看看他们是如何做到的!

一个名为 EchoService 的 gRPC 应用程序

在这篇文章中,我们将从 gRPC API 定义构建一个小的概念验证 gRPC 应用程序,添加一个 REST 服务网关,并最终在单个 TLS 端口上提供所有服务。该应用程序名为 EchoService,是 shell 命令 `echo` 的 Web 等效项:该服务返回,或“回显”,任何发送给它的文本。

首先,让我们在名为 EchoMessage 的 protobuf 消息中定义 EchoService 的参数,该消息包含一个名为 value 的字段。我们将在名为 service.proto 的 protobuf “.proto” 文件中定义此消息。以下是我们的 EchoMessage

message EchoMessage {
 string value = 1;
}

在同一个 .proto 文件中,我们定义了一个 gRPC 服务,该服务接收并返回此数据结构

service EchoService {
  rpc Echo(EchoMessage) returns (EchoMessage) {
  }
}

通过协议缓冲区编译器 protoc “原样”运行此 service.proto 文件,会在 Go 中生成一个 gRPC 服务存根,以及各种语言的客户端。但是,仅有 gRPC 不如同时公开 REST 接口的服务有用,因此我们不会止步于 gRPC 服务存根。

接下来,我们添加 gRPC REST 网关。该库将在 gRPC EchoService 的基础上构建一个 RESTful 代理。为了构建此网关,我们向 EchoService .proto 添加元数据,以指示 Echo RPC 映射到 RESTful POST 方法,所有 RPC 参数都映射到 JSON 正文。该网关可以将 RPC 参数映射到 URL 路径和查询参数,但为了简洁起见,我们在此省略了这些复杂性。

service EchoService {
  rpc Echo(EchoMessage) returns (EchoMessage) {
    option (google.api.http) = {
      post: "/v1/echo"
      body: "*"
    };
  }
}

这意味着网关一旦由 protoc 生成,现在就可以接受来自 curl 的 HTTP 请求,如下所示

curl -X POST -k https://:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'

到目前为止,整个系统看起来是这样的:一个 service.proto 文件同时生成一个 gRPC 服务器和一个 REST 代理

gRPC API with REST gateway

为了将这一切整合在一起,echo 服务会创建一个 Go http.Handler 来检测协议是否为 HTTP/2 以及 Content-Type 是否为“application/grpc”,并将此类请求发送到 gRPC 服务器。所有其他请求都被路由到 REST 网关。代码如下所示

if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
	grpcServer.ServeHTTP(w, r)
} else {
	otherHandler.ServeHTTP(w, r)
}

要试用它,您只需一个可用的 Go 1.6 开发环境和以下简单命令

go get -u github.com/philips/grpc-gateway-example
grpc-gateway-example serve

服务器运行后,您可以在 HTTP 1.1 和 gRPC 接口上尝试请求

grpc-gateway-example echo Take a REST from REST with gRPC
curl -X POST -k https://:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'

最后一个额外福利:由于我们有开放 API 规范,如果您的笔记本电脑上正在运行上述服务器,您可以访问 https://:10000/swagger-ui/#!/EchoService/Echo 来浏览开放 API UI。

gRPC/REST Open API document

我们已经了解了如何使用 gRPC 连接 REST 世界。如果您想查看完整的项目,请访问 GitHub 上的仓库。我们认为这种使用单个 protobuf 来描述 API 的模式,可以带来易于使用、灵活的 API 框架,我们很高兴能在更多项目中利用它。