我们今天的客座文章来自 CoreOS 的 Brandon Phillips。CoreOS 为 Linux 容器构建开源项目和产品。他们用于共识和发现的旗舰产品(etcd)和他们的容器引擎rkt是gRPC的早期采用者,gRPC 是一个基于 protobufs 和 HTTP/2 的 RPC 框架,可以使构建和使用 API 更轻松且性能更高。由于许多客户端都使用 http/1.1 加 JSON,因此与 JSON 和开放 API 的互操作性非常重要。对于更习惯于基于 HTTP/1.1+JSON 的 API 和开放 API 倡议规范(以前是 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 网关。代码看起来像这样
如果 r.ProtoMajor 等于 2 并且 strings.Contains(r.Header.Get(“Content-Type”), “application/grpc”) 为真,则 grpcServer.ServeHTTP(w, r);否则,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 内核开发人员。作为俄勒冈州立大学开源实验室的毕业生,他对开源技术充满热情。