概述
在前面调研的 浏览器 Push 的方案 中我总结了若干种可能支持浏览器接收 Server Push 的方式,虽然不是所有的方式都可以 work,但是还是很有启发作用。但是,其中有一种我没有太了解清楚的方式就是 gRPC-Web 是如何实现的,所以在本文中,我就想了解一下 gRPC-Web 是如何实现 Bidirectional Stream 的。
gRPC Web 支持的特性
截止到目前为止,gRPC Web 只支持两种类型的调用,分别是:
- Unary RPC 调用(一个请求一个响应模型)
- Server Streaming 调用(一个请求,但是多个响应模型)
但是,并不是说你可以直接用 gRPC Web 直接调用一个普通的其他语言的 gRPC Server,因为他们协议是不一样的,所以中间需要一个 gRPC Web 代理,官方推荐的就是 Envoy,并且官方也将 Envoy 作为官方的实现方式进行迭代。
gRPC Web 的协议实现
刚才提了一下,gRPC Web 不能直接和普通的 gRPC Server 直接通信,因为他们的协议不一样。我们都知道普通的 gRPC 都是基于 HTTP2 的实现,但是 gRPC Web 却是基于 HTTP/1.1 的实现,所以这中间需要一个 Proxy 进行转换,因此,这就是需要 Envoy 这样的中间件来转换协议了。
其实在我之前的调研里面,我发现 Websocket 应该还是现在唯一能够稳定支持 Bidirectional Stream 的浏览器方式,但是为什么 gRPC Web 不使用 Websocket 呢?这个问题在 gRPC Web 的官方文档中给出了解答:Websocket 和 HTTP 不兼容,尤其是随处可见的 Web 基础设施。但是就我个人而言,其实我是不赞同的,因为就我的使用和见过的场景来说(2B 和 2C 的场景都有),Websocket 的支持随处可见,并且难度没有想象中的大。
你说因为 Websocket 不能开箱即用(让其他的 Web 基础设施都能支持),但是现在的 gRPC Web 实现我觉得也并没有很好,因为你只支持了 unary 和 Server Streaming,那么换成 Websocket 并且能够支持完整的 gRPC 功能会不会更好呢?
一个示例
OK,前面介绍了一些理论层面的东西,下面就按照官方的指导,我们运行一个 gRPC Web 的示例,并且看下请求是否真的就如同前面的分析一般实现:
[root@liqiang.io]# git clone https://github.com/grpc/grpc-web
[root@liqiang.io]# cd grpc-web
[root@liqiang.io]# docker-compose pull prereqs node-server envoy commonjs-client
[root@liqiang.io]# docker-compose up -d node-server envoy commonjs-client
图 1:gRPC Web 请求 |
---|
图 2:gRPC Web 请求成功内容 |
---|
从这个简单的示例中可以看到,这个请求是通过 HTTP Post 请求发起的,然后被后端的 Envoy 接收,然后转换成通用的 gRPC 协议;然后在收到响应之后,然后再转换成 Post 的响应,但是 Post 的响应是一个编码后的字符串,还需要进行解码:
图 3:gRPC 响应解码 |
---|
小结
Ok,这就是我对 gRPC Web 的一个简单了解,从这个了解中可以看到 gRPC Web 的实现还是比较不方便的,并且支持的特性也比较单一,还需要 proxy 支持,那既然都 proxy 支持了,那么我为什么不上 API 网关?但是作为一个学习也无妨就是了。