我们今天的客座文章来自 CoreOS 的 Brandon Phillips。 CoreOS 为 Linux 容器构建开源项目和产品。他们用于共识和发现的旗舰产品 (etcd) 以及他们的容器引擎 rkt 是 gRPC 的早期采用者,gRPC 是一个基于 protobufs 和 HTTP/2 的 RPC 框架,可以使构建和使用 API 变得更容易和更高效。由于许多客户端使用 http/1.1 加 json,因此与 JSON 和 Open API 的互操作性非常重要。对于更习惯于基于 HTTP/1.1+JSON 的 API 和 Open API Initiative 规范(以前称为 swagger)API 的用户,他们正在使用开源库的组合来以这种形式提供他们的 gRPC 服务。他们构建了 API 多路复用器,以使用户获得两全其美。让我们深入了解细节,并找出他们是如何做到的!
这篇文章最初发表在 CoreOS 博客上 (链接)。我们在此重新发布,并进行了一些编辑。
CoreOS 选择 gRPC 的关键原因之一是它使用 HTTP/2,使应用程序能够在一个 TCP 端口上同时提供 HTTP 1.1 REST+JSON API 和高效的 gRPC 接口。(适用于 Go)这使开发人员能够与 REST Web 生态系统兼容,同时推进一种新的、高效的 RPC 协议。随着最近发布的 Go 1.6,Go 默认情况下附带了一个稳定的 net/http2 包。
一个名为 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; }
在同一个 protobuf 文件中,我们定义了一个 gRPC 服务,它接收此数据结构并返回它
service EchoService { rpc Echo(EchoMessage) returns (EchoMessage) { } }
通过 Protocol Buffer 编译器 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 的类似这样的请求
curl -X POST -k https://localhost:10000/v1/echo -d ‘{“value”: “CoreOS is hiring!”}’
到目前为止,整个系统看起来像这样,一个 service.proto 文件既生成 gRPC 服务器又生成 REST 代理
带有 REST 网关的 gRPC API
为了将所有这些整合在一起,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 使用 gRPC 从 REST 中休息 curl -X POST -k https://localhost:10000/v1/echo -d ‘{“value”: “CoreOS is hiring!”}’
最后一个奖励:因为我们有 OpenAPI 规范,所以如果您在笔记本电脑上运行上面的服务器,则可以浏览位于 https://localhost:10000/swagger-ui/#!/EchoService/Echo 的 OpenAPI UI。
gRPC/REST OpenAPI 文档
我们已经了解了如何使用 gRPC 桥接到 REST 世界。如果您想查看完整的项目,请查看 GitHub 上的仓库。我们认为这种使用单个 protobuf 来描述 API 的模式可以带来易于使用、灵活的 API 框架,我们很高兴在更多项目中利用它。
—
关于作者
Brandon Phillips(CoreOS)
Brandon Philips 作为 CTO 正在帮助 CoreOS 构建现代 Linux 服务器基础设施。在加入 CoreOS 之前,他在 Rackspace 从事云监控工作,并在 SUSE 担任 Linux 内核开发人员。作为俄勒冈州立大学开源实验室的毕业生,他对开源技术充满热情。