概述

在前面调研的 浏览器 Push 的方案 中我总结了若干种可能支持浏览器接收 Server Push 的方式,虽然不是所有的方式都可以 work,但是还是很有启发作用。但是,其中有一种我没有太了解清楚的方式就是 gRPC-Web 是如何实现的,所以在本文中,我就想了解一下 gRPC-Web 是如何实现 Bidirectional Stream 的。

gRPC Web 支持的特性

截止到目前为止,gRPC Web 只支持两种类型的调用,分别是:

但是,并不是说你可以直接用 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 的示例,并且看下请求是否真的就如同前面的分析一般实现:

  1. [root@liqiang.io]# git clone https://github.com/grpc/grpc-web
  2. [root@liqiang.io]# cd grpc-web
  3. [root@liqiang.io]# docker-compose pull prereqs node-server envoy commonjs-client
  4. [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 网关?但是作为一个学习也无妨就是了。