gRPC 与 REST 和 Open API
今天的客座博文来自 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 和 Open API Specification(前称 Swagger)的轻松互操作性极具价值。对于那些更熟悉基于 HTTP/1.1+JSON 和 Open API Spec 的 API 的用户,他们使用了一些开源库的组合,使其 gRPC 服务同时支持 gRPC 和 HTTP REST 风格,使用 API 复用器让用户获得两全其美的体验。让我们深入了解细节,看看他们是如何做到的!
一个名为 EchoService 的 gRPC 应用
在这篇博文中,我们将构建一个小的概念验证 (proof-of-concept) gRPC 应用,从一个 gRPC API 定义开始,添加一个 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) {
}
}
直接运行这个 service.proto
文件通过 Protocol Buffer 编译器 protoc
,会生成一个 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://localhost:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'
到目前为止,整个系统看起来像这样,一个单独的 service.proto
文件同时生成 gRPC 服务器和 REST 代理

为了将这一切整合在一起,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://localhost:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'
最后一点额外内容:因为我们有一个 Open API 规范,如果您的笔记本电脑上正在运行上述服务器,您可以浏览运行在 https://localhost:10000/swagger-ui/#!/EchoService/Echo
的 Open API UI。

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