浏览器中 gRPC 的状态
gRPC 1.0 于 2016 年 8 月发布,此后已发展成为应用程序通信的首要技术解决方案之一。它已被全球的初创公司、企业公司和开源项目采用。它对多语言环境的支持、对性能的关注、类型安全性和开发人员生产力已经改变了开发人员设计其架构的方式。
到目前为止,这些好处主要仅适用于移动应用程序和后端开发人员,而前端开发人员则不得不继续依赖 JSON REST 接口作为其主要的信息交换方式。但是,随着 gRPC-Web 的发布,gRPC 有望成为前端开发人员工具箱中的宝贵补充。
在这篇文章中,我将描述 gRPC 在浏览器中的一些历史,探索当今世界的现状,并分享一些关于未来的想法。
开端
在 2016 年夏天,Google 和 Improbable1 的两个团队分别开始着手实施可以称为“用于浏览器的 gRPC”的东西。他们很快发现了彼此的存在,并聚在一起为新协议定义了一个规范2。
gRPC-Web 规范
目前,无法在浏览器中实现 HTTP/2 gRPC 规范3,因为根本没有浏览器 API 对请求进行足够细粒度的控制。例如:没有办法强制使用 HTTP/2,即使有,也无法在浏览器中访问原始的 HTTP/2 帧。gRPC-Web 规范从 HTTP/2 规范的角度出发,然后定义了差异。这些差异尤其包括
- 支持 HTTP/1.1 和 HTTP/2。
- 在请求/响应正文的末尾发送 gRPC 尾部,如 gRPC 消息头中的新位所示4。
- 用于在 gRPC-Web 请求和 gRPC HTTP/2 响应之间进行转换的强制代理。
技术
基本思想是让浏览器发送正常的 HTTP 请求(使用 Fetch 或 XHR),并在 gRPC 服务器前面有一个小型代理来将请求和响应转换为浏览器可以使用的内容。


两个实现
Google 和 Improbable 的团队都继续在两个不同的存储库5,6中实现了该规范,并且实现略有不同,因此两者都不完全符合规范,并且在很长一段时间内,两者都不与彼此的代理兼容7,8。
Improbable gRPC-Web 客户端9 使用 TypeScript 实现,并以 @improbable-eng/grpc-web
的形式发布在 npm 上10。 还有一个 Go 代理可用,它既可以作为包导入到现有的 Go gRPC 服务器中11,也可以作为独立的代理,用于将任意 gRPC 服务器暴露给 gRPC-Web 前端12。
Google gRPC-Web 客户端13 使用 Google Closure 库14 以 JavaScript 实现。它以 grpc-web
的形式发布在 npm 上15。最初它附带一个以 NGINX 扩展形式实现的代理16,但后来转而使用 Envoy 代理 HTTP 过滤器17,该过滤器自 v1.4.0 版本以来在所有版本中都可用。
功能集
gRPC HTTP/2 实现都支持四种方法类型:一元 (unary)、服务器端流式 (server-side)、客户端流式 (client-side) 和双向流式 (bi-directional)。然而,gRPC-Web 规范并未明确要求任何客户端或双向流式支持,只规定一旦 WHATWG Streams18 在浏览器中实现,就会实现这些功能。
Google 客户端支持一元和服务器端流式,但仅当与 grpcwebtext
模式一起使用时才支持。 grpcweb
模式仅完全支持一元请求。这两种模式指定了在请求和响应中编码 protobuf 有效负载的不同方式。
Improbable 客户端支持一元和服务器端流式,并具有一个基于浏览器功能自动选择 XHR 和 Fetch 的实现。
以下是一个总结所支持的不同功能的表格
客户端 / 功能 | 传输 | 一元 | 服务器端流 | 客户端和双向流 |
---|---|---|---|---|
Improbable | Fetch/XHR ️ | ✔️ | ✔️ | ❌19 |
Google (grpcwebtext ) | XHR ️ | ✔️ | ✔️ | ✔️ |
Google (grpcweb ) | XHR ️ | ✔️ | ❌20 | ✔️ |
有关此表的更多信息,请参阅 我在 github 上的兼容性测试仓库。
兼容性测试将来可能会发展成为一些自动测试框架,以强制执行和记录各种兼容性。
兼容性问题
当然,两个不同的代理也会带来兼容性问题。幸运的是,这些问题最近已得到解决,因此您可以期望将任一客户端与任一代理一起使用。
未来
Google 实现于 2018 年 10 月宣布 1.0 版本和全面可用性21,并发布了未来目标的路线图22,包括
- 一种高效的类 JSON 消息编码
- 适用于 Node、Python、Java 等的进程内代理
- 与流行的框架(React、Angular、Vue)集成
- 用于内存高效流的 Fetch API 传输
- 双向流式支持
Google 正在征求社区对哪些功能重要的反馈,因此,如果您认为其中任何一个对您特别有价值,请填写他们的调查23。
两个项目最近的会谈已达成协议,将推广 Google 客户端和 Envoy 代理作为新用户的首选解决方案。Improbable 客户端和代理将保留作为规范的替代实现,而不依赖于 Google Closure,但应被视为实验性的。将为现有用户提供迁移指南以迁移到 Google 客户端,并且团队正在共同努力以统一生成的 API。
结论
Google 客户端将继续以稳定的速度实施新功能和修复,由一个致力于其成功的团队负责,并且它是官方 gRPC 客户端。 它没有像 Improbable 客户端那样的 Fetch API 支持,但如果这是社区的重要功能,则将添加该功能。 Google 团队和更广泛的社区正在合作开发官方客户端,以造福整个 gRPC 社区。 自 GA 发布以来,社区对 Google gRPC-Web 仓库的贡献显着增加。
在选择两个代理时,在功能上没有区别,因此这成为您部署模型的问题。Envoy 将适合某些场景,而进程内的 Go 代理则具有其自身的优势。
如果您今天开始使用 gRPC-Web,请首先尝试 Google 客户端。它具有严格的 API 兼容性保证,并且基于 Gmail 和 Google 地图使用的坚如磐石的 Google Closure 库基础。 如果您需要 Fetch API 内存效率或实验性的 websocket 客户端和双向流,则 Improbable 客户端是一个不错的选择,并且在可预见的将来,Improbable 将继续使用和维护它。
无论哪种方式,gRPC-Web 都是 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 已正式发布 ↩
- github.com/grpc/grpc-web/blob/master/doc/roadmap.md ↩
- docs.google.com/forms/d/1NjWpyRviohn5jaPntosBHXRXZYkh_Ffi4GxJZFibylM ↩