浏览器中 gRPC 的现状
gRPC 1.0 于 2016 年 8 月发布,此后已发展成为应用程序通信领域首屈一指的技术解决方案之一。它已被全球各地的初创公司、企业和开源项目所采用。其对多语言环境的支持、对性能的关注、类型安全以及开发人员的工作效率,彻底改变了开发人员设计架构的方式。
到目前为止,这些好处主要由移动应用程序和后端开发人员享有,而前端开发人员不得不继续依赖 JSON REST 接口作为其主要的交换信息手段。然而,随着 gRPC-Web 的发布,gRPC 有望成为前端开发人员工具箱中的重要补充。
在这篇文章中,我将介绍 gRPC 在浏览器中的一些历史,探讨当前的现状,并分享一些关于未来的思考。
起源
2016 年夏天,谷歌的一个团队和 Improbable1 的一个团队独立开始致力于实现一种可以称为“浏览器版 gRPC”的技术。他们很快发现了彼此的存在,并聚在一起为该新协议定义了一份规范2。
gRPC-Web 规范
目前在浏览器中实现 HTTP/2 gRPC 规范3 是不可能的,因为根本没有任何浏览器 API 能提供对请求足够的细粒度控制。例如:无法强制使用 HTTP/2,即使可以,浏览器也无法访问原始的 HTTP/2 帧。gRPC-Web 规范从 HTTP/2 规范的角度出发,然后定义了其中的差异。这些差异显著包括:
- 同时支持 HTTP/1.1 和 HTTP/2。
- 在请求/响应体的末尾发送 gRPC 尾部(trailers),正如 gRPC 消息头中的一个新位所指示的那样4。
- 强制使用代理,用于在 gRPC-Web 请求和 gRPC HTTP/2 响应之间进行转换。
技术细节
其基本理念是让浏览器发送常规的 HTTP 请求(使用 Fetch 或 XHR),并在 gRPC 服务器前放置一个小代理,将请求和响应转换为浏览器可以使用的格式。


两种实现
谷歌和 Improbable 的团队分别在两个不同的存储库5,6中实现了该规范。由于实现方式略有不同,导致两者都不完全符合规范,很长一段时间内,双方的代理也互不兼容7,8。
Improbable 的 gRPC-Web 客户端9 使用 TypeScript 实现,并作为 npm 包 @improbable-eng/grpc-web10 提供。此外还有一个 Go 代理,既可以作为包导入到现有的 Go gRPC 服务器中11,也可以作为独立代理使用,将任意 gRPC 服务器暴露给 gRPC-Web 前端12。
谷歌的 gRPC-Web 客户端13 使用 Google Closure 库14 基础进行 JavaScript 实现。它以 grpc-web15 的身份在 npm 上发布。它最初附带一个实现为 NGINX 扩展的代理16,但后来加大了对 Envoy 代理 HTTP 过滤器的投入17,该过滤器自 v1.4.0 版本以来的所有版本中都可用。
功能集
所有的 gRPC HTTP/2 实现都支持四种方法类型:一元(Unary)、服务端流式(Server-side streaming)、客户端流式(Client-side streaming)和双向流式(Bi-directional streaming)。但是,gRPC-Web 规范并未专门强制要求任何客户端流式或双向流式支持,仅规定在浏览器中实现 WHATWG Streams18 后将对其进行实现。
谷歌客户端支持一元和服务端流式,但仅在使用 grpcwebtext 模式时可用。在 grpcweb 模式下,仅完全支持一元请求。这两种模式规定了在请求和响应中编码 protobuf 负载的不同方式。
Improbable 客户端支持一元和服务端流式,并且具有一种根据浏览器功能自动在 XHR 和 Fetch 之间进行选择的实现。
下表总结了所支持的不同功能:
| 客户端 / 功能 | 传输 | 一元 (Unary) | 服务端流式 | 客户端与双向流式 |
|---|---|---|---|---|
| Improbable | Fetch/XHR ️ | ✔️ | ✔️ | ❌19 |
Google (grpcwebtext) | XHR ️ | ✔️ | ✔️ | ❌ |
Google (grpcweb) | XHR ️ | ✔️ | ❌20 | ❌ |
有关此表的更多信息,请参阅我在 GitHub 上的兼容性测试仓库。
未来,这些兼容性测试可能会发展成为某种自动化测试框架,以强制执行并记录各种兼容性情况。
兼容性问题
当然,由于有两种不同的代理,也存在兼容性问题。幸运的是,这些问题最近已经得到解决,因此您可以放心地将任何客户端与任何代理一起使用。
未来
谷歌的实现于 2018 年 10 月宣布 1.0 版本并正式发布(GA)21,并发布了未来目标的路线图22,其中包括:
- 高效的类似 JSON 的消息编码
- 适用于 Node、Python、Java 等语言的进程内(In-process)代理
- 与流行框架(React、Angular、Vue)的集成
- 用于内存高效流式传输的 Fetch API 传输
- 对双向流式的支持
谷歌正在寻求反馈,以了解哪些功能对社区很重要,因此如果您认为其中任何功能对您特别有价值,请填写他们的问卷调查23。
两个项目之间最近的会谈已达成共识,将推广谷歌客户端和 Envoy 代理作为新用户的首选解决方案。Improbable 的客户端和代理将保留作为该规范的替代实现,不再依赖 Google Closure,但应被视为实验性的。将为现有用户提供一份迁移指南,帮助他们转向谷歌客户端,团队也正在共同努力收敛生成的 API。
结论
谷歌客户端将继续稳步实施新功能和修复,并拥有专门的团队致力于其成功,且作为官方的 gRPC 客户端。它目前不像 Improbable 客户端那样支持 Fetch API,但如果这对社区来说是一个重要的功能,它将被添加。谷歌团队和广大社区正在协作完善官方客户端,这将使整个 gRPC 社区受益。自 GA 公告发布以来,社区对谷歌 gRPC-Web 仓库的贡献显著增加。
在选择两种代理时,功能上没有区别,因此这取决于您的部署模型。Envoy 将适合某些场景,而进程内 Go 代理则有其自身的优势。
如果您现在开始使用 gRPC-Web,请首先尝试谷歌客户端。它具有严格的 API 兼容性保证,并建立在 Gmail 和 Google Maps 所使用的稳如磐石的 Google Closure 库基础之上。如果您需要 Fetch API 的内存效率或者实验性的 websocket 客户端和服务端双向流式传输,Improbable 客户端是一个不错的选择,并且在可预见的未来,它将继续由 Improbable 使用和维护。
无论如何,对于 Web 开发人员来说,gRPC-Web 都是一个绝佳的选择。它将复杂协议的可移植性、性能和工程优势带到了浏览器中,这对于前端开发人员来说是一个激动人心的时代!
参考资料
- improbable.io/games/blog/grpc-web-moving-past-restjson-towards-type-safe-web-apis ↩
- github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md ↩
- github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md ↩
- github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2 ↩
- github.com/improbable-eng/grpc-web ↩
- github.com/grpc/grpc-web ↩
- github.com/improbable-eng/grpc-web/issues/162 ↩
- github.com/grpc/grpc-web/issues/91 ↩
- github.com/improbable-eng/grpc-web/tree/master/client/grpc-web ↩
- npmjs.com/package/@improbable-eng/grpc-web ↩
- github.com/improbable-eng/grpc-web/tree/master/go/grpcweb ↩
- github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy ↩
- github.com/grpc/grpc-web/tree/master/javascript/net/grpc/web ↩
- developers.google.com/closure ↩
- npmjs.com/package/grpc-web ↩
- github.com/grpc/grpc-web/tree/master/net/grpc/gateway ↩
- envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_web_filter ↩
- streams.spec.whatwg.org ↩
- Improbable 客户端通过实验性的 websocket 传输支持客户端和服务端双向流式传输。这不属于 gRPC-Web 规范的一部分,且不建议在生产环境中使用。 ↩
-
grpcweb允许调用服务端流式方法,但在流关闭之前不会返回数据。 ↩ - gRPC-Web 现已正式发布(Generally Available) ↩
- github.com/grpc/grpc-web/blob/master/doc/roadmap.md ↩
- docs.google.com/forms/d/1NjWpyRviohn5jaPntosBHXRXZYkh_Ffi4GxJZFibylM ↩