本节摘要:gRPC-Web 是浏览器接入 gRPC 服务的标准方案:浏览器跑受限的 gRPC-Web 协议,代理层把它转译成标准 gRPC 转发给服务端。本节讲浏览器直连受限的技术根源、gRPC-Web 的架构与能力边界(客户端流不可用、双向流退化)、代理层的选择与配置要点,以及另一条对外兼容路线——HTTP 转码注解让 gRPC 服务自动提供 REST 接口。
阅读完本节,你应当能够:
第 1 章选型对比里埋过一句话:浏览器无法直接发起标准 gRPC 调用。现在兑现解释。
标准 gRPC 对 HTTP/2 的使用方式,超出了浏览器开放给 JavaScript 的能力范围:浏览器端的接口能发 HTTP/2 请求,但不暴露底层流的控制权——脚本无法按 gRPC 的需要读写原始 DATA 帧、无法在请求体流式上传的同时读取响应(全双工)、对 trailers 的读取支持也不完整。这些能力缺口不是疏忽,而是浏览器安全模型的有意约束。
于是现实分成了两半:服务端与移动端可以自由使用完整 gRPC;浏览器被困在"能用 HTTP/1.1 与受限 HTTP/2"的笼子里。gRPC-Web 就是专为笼子设计的受限方言。
gRPC-Web 的核心设计是"浏览器讲受限方言,代理当翻译":
受限体现在两端。浏览器侧:客户端不能开客户端流(上传方向只能一次性发完整请求体);双向流退化为"服务端流"形态——浏览器发一次请求、收一串响应。服务端侧:代理层把标准 gRPC 的流式响应转成浏览器能消化的形态(应用层分帧封装, trailers 内容改放在 body 尾部的自定义块里,绕开浏览器对 trailers 支持的不一致)。
代理是必需品。Envoy 原生支持 gRPC-Web 转译过滤器(配置一段即可启用),也有独立的 gRPC-Web 网关实现。代理的选型与部署沿用 5.2 节代理负载均衡的账:它是基础设施层,要有冗余与容量规划;它通常与边缘网关合部(一个 Envoy 同时干 TLS 终结、路由、gRPC-Web 转译三件事是常见形态)。
gRPC-Web 的边界清单与对策:
| 能力 | gRPC-Web 支持度 | 工程对策 |
|---|---|---|
| 一元调用 | 完整支持 | 直接用 |
| 服务端流 | 支持 | 推送类需求的主通道 |
| 客户端流 | 不支持 | 改批量消息(repeated)一次性上传,或走 REST 上传接口 |
| 双向流 | 退化为服务端流 | 上行用多次独立调用,下行用服务端流拼合 |
| 自定义元数据 | 部分支持 | 认证令牌走标准头部传递,复杂元数据避免依赖 |
| 压缩 | 受限 | 大数据量考虑应用层分页或改走专门下载通道 |
典型落地形态:管理后台、数据看板这类内部前端,用 gRPC-Web 直连内部服务,省掉"为前端单独写一层 REST 适配服务"的重复劳动;对外公开的网站,谨慎评估——公网用户的环境多样性(老旧代理、企业防火墙)对方言流量的兼容性是未知数,转码出标准 REST 更稳。
前端工程化方面,gRPC-Web 的代码生成链路与标准 gRPC 一致(proto 进、TypeScript 客户端出),配合类型定义的收益明显——前端拿到的不再是手写接口封装,而是与后端同源的强类型客户端。这是团队选择 gRPC-Web 的最大动力之一:契约的单一事实源延伸到了浏览器端。
与手写前端接口层对照,这套链路省掉的是三类长期成本:接口对齐成本(后端改字段,前端类型立刻编译报错,而不是运行时 undefined);文档同步成本(类型即文档,接口文档从"要维护的负债"变成"生成的产物");联调返工成本(契约在先,双方对着同一份定义开发,联调期从"发现理解不一致"变成"确认实现一致")。代价是前端构建链变重(生成步骤进构建流程)与团队学习成本——对两三个人、一两个页面的轻量前端,这笔账可能不划算;对长生命周期的中后台系统,几乎稳赚。
还有个容易被忽略的细节:生成的客户端天然带流式接口,前端处理服务端推送(进度更新、日志滚动)不再需要引入另一套通信机制——同一套 SDK 覆盖一元与流式,技术栈的异构度随之下降。
浏览器之外,第三方开发者与开放 API 的需求(第 1 章的选型边界)是另一类"进不来的消费者"。HTTP 转码(transcoding)解决它:在 proto 里用注解声明方法与 HTTP 的映射关系,转码网关(Envoy、专用网关或各云 API 网关)据此把 REST 请求翻译成 gRPC 调用。
注解形态示意(概念级):
service OrderQuery { // 转码声明:GET 路径参数映射到请求字段 rpc GetOrder (GetOrderRequest) returns (Order) { option (google.api.http) = { get: "/v1/orders/{order_id}" }; } // 转码声明:POST 请求体映射到请求消息 rpc CreateOrder (CreateOrderRequest) returns (Order) { option (google.api.http) = { post: "/v1/orders" body: "*" }; } }
网关读到注解后:外部 GET 请求按路径规则填充请求消息、转成 gRPC 调用后端、响应再按注解映射回 JSON。后端只维护一套 gRPC 实现,对外同时提供 REST 与 gRPC 两副面孔。
转码的代价与纪律:

把浏览器与外部接入的三个可选方案放到一张决策表里:
| 方案 | 谁接入 | 优点 | 代价 | 典型场景 |
|---|---|---|---|---|
| gRPC-Web 网关 | 自有网页前端 | 契约直达前端、强类型 | 方言与代理、流式受限 | 内部管理系统、看板 |
| HTTP 转码网关 | 第三方、外部系统 | 标准 REST 零门槛、生态惯用 | 注解维护、双协议成本 | 开放 API、对外合作 |
| 前端专属 REST 服务 | 前端需求与后端模型差异大时 | 页面形态接口自由裁剪 | 多一层服务要开发运维 | 页面聚合、复杂 BFF |
第三个方案(为前端单独写聚合服务)不该被前两个的先进性吓退:当前端需要的是"页面级聚合数据"而非"单资源 CRUD"时,BFF 形态依然是最贴合的——gRPC-Web 解决的是通信协议问题,不是接口形态问题。一个实用的组合形态是:BFF 服务自身对上游用 gRPC(吃到内部通信的全部红利),对前端用 gRPC-Web 暴露(契约与类型直达浏览器)——两种技术在同一架构里各司其职,并不互斥。
选型时还要评估团队的网关运维能力:gRPC-Web 与 HTTP 转码都要求边缘有可控的代理层(配置、升级、监控),完全没有网关运维经验的团队直接上,第一个踩的坑往往是"代理不支持 HTTP/2 后端"或"trailer 被吞"这类基础设施问题。先解决边缘能力,再谈协议打通。
⚠️ 常见坑:gRPC-Web 上线后间歇性断流,最后定位是链路上某台老旧代理对分块响应的超时处理有问题。gRPC-Web 的长响应在中间设备眼里是"一直没传完的 HTTP 响应",公网链路上的中间设备行为不可控——内部环境可控才敢放心用方言,公网走转码出的标准 REST。
💡 关键直觉:gRPC-Web 与 HTTP 转码的共同哲学是"核心不动、边缘适配"——不让浏览器的约束污染服务端设计,也不让服务端的先进性强加给外部消费者。适配设施全部放网关层,后端保持纯粹。
问:gRPC-Web 能上生产了吗?
能,它不是实验性技术——主流代理与各语言的代码生成链路都稳定支持。要评估的是你自己的环境:代理层建设、前端工具链、以及消费方的网络路径是否可控。
问:转码网关会不会成为性能瓶颈?
JSON 序列化确实是转码的固有成本,但开放 API 的流量形态(第三方调用频率有限)通常远低于内部调用量级,瓶颈论在多数场景不成立。真到量级,边缘多实例横向扩即可。
问:一个服务能同时开 gRPC、gRPC-Web、转码 REST 三副面孔吗?
架构上可以(网关层各配各的过滤器),但要克制——每副面孔都是一份要测试、要文档、要向后兼容的承诺。从实际消费方出发,没消费方的面孔不要开。
问:gRPC-Web 的代理和转码代理可以是同一个吗?
可以且推荐——Envoy 一类网关能在同一边缘同时挂 gRPC-Web 过滤器与转码配置,对外分别暴露方言端点与 REST 端点。统一在一个网关上管理,TLS、限流、观测也只需要配一套,运维成本显著低于两套独立代理。
最后一节收束全书:服务网格如何把治理下沉成基础设施,契约如何体面地活过多年演进,以及 gRPC 在 HTTP/3 与 AI 时代往哪里走。