gRPC-Gateway 与 gRPC-Web 跨生态互通¶
在构建企业级微服务时,内部系统间使用 gRPC 可以获得极致性能。但在面向外部浏览器前端、移动端第三方开发者或遗留系统时,直接暴露原始 gRPC 往往会面临协议兼容性阻碍。
本章介绍两大主流桥接方案:面向 HTTP/REST 的 gRPC-Gateway 与面向前端浏览器的 gRPC-Web。
1. 为什么浏览器不能直接调用原生 gRPC?¶
浏览器环境中的 JavaScript 标准 API(fetch 或 XMLHttpRequest)无法直接操作底层的 HTTP/2 通信细节:
1. 无法读取 HTTP/2 Trailers(尾部帧):gRPC 将至关重要的调用状态码(grpc-status)存放在响应结尾的 Trailer 中,而浏览器 API 默认会将 Trailers 过滤或丢弃。
2. 无法细粒度控制帧流:浏览器无法控制原生二进制 Length-prefixed 帧的半关闭(Half-Close)时机。
因此,面向前端与外部开放时,必须引入协议转换层。
2. 方案一:gRPC-Gateway (同时暴露 REST & gRPC)¶
grpc-gateway 是 Google 推荐的事实标准反向代理插件。它读取 .proto 中的注解配置,自动生成一个将 HTTP/JSON 请求实时翻译为 gRPC 调用 的高性能代理服务,并能同步生成 OpenAPI (Swagger) 规范文档。
graph LR
subgraph Clients ["多端请求"]
RESTClient["第三方开发者 / Web 前端 (HTTP/JSON)"]
GRPCClient["内部微服务 (gRPC / HTTP/2)"]
end
subgraph Gateway ["gRPC-Gateway 代理层"]
Transcoder["协议转码器 (JSON <-> Protobuf 序列化)"]
end
subgraph Backends ["后端 gRPC 微服务"]
CoreSvc["核心业务微服务 (:50051)"]
end
RESTClient -->|POST /v1/orders| Transcoder
Transcoder -->|gRPC: OrderService.CreateOrder| CoreSvc
GRPCClient ==>|原生直连 gRPC| CoreSvc
编写支持 REST 注解的 .proto 文件¶
需引入 Google 官方的 google/api/annotations.proto:
syntax = "proto3";
package commerce.order.v1;
import "google/api/annotations.proto";
option go_package = "example.com/commerce/order/v1;orderv1";
service OrderService {
// 定义 gRPC 方法并通过 http 注解映射为 RESTful 路径
rpc GetOrder (GetOrderRequest) returns (GetOrderResponse) {
option (google.api.http) = {
get: "/v1/orders/{order_id}" // 路径参数自动绑定到请求字段
};
}
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse) {
option (google.api.http) = {
post: "/v1/orders"
body: "*" // 请求体 JSON 自动绑定到整条 CreateOrderRequest
};
}
}
message GetOrderRequest {
string order_id = 1;
}
message GetOrderResponse {
string order_id = 1;
string status = 2;
double amount = 3;
}
核心收益¶
- 单一契约源(Single Source of Truth):一份
.proto文件同时驱动内部 RPC、外部 REST API 与前端 Swagger 文档,杜绝文档与实现脱节。 - 高性能同进程集成:在 Go 语言中,gRPC-Gateway 可以与 gRPC 服务编译进同一个二进制可执行文件中,通过同一个端口或独立端口监听,几乎无额外运维负担。
3. 方案二:gRPC-Web (前端原生调用)¶
如果前端研发团队希望在 TypeScript / React / Vue 中享受强类型检查与自动生成的客户端桩代码,可以采用 gRPC-Web 方案。
graph LR
Browser["浏览器 Web 应用 (TypeScript / gRPC-Web Client)"]
Envoy["Envoy 网关 (内置 grpc_web 过滤器)"]
Service["后端 gRPC 节点 (原生 HTTP/2 gRPC)"]
Browser -->|HTTP/1.1 或 HTTP/2 (base64 或二进制)| Envoy
Envoy -->|标准 gRPC (Trailers 转换)| Service
Service -->> Envoy
Envoy -->>|将 Trailers 编码进响应 body| Browser
工作机制¶
- 前端使用
protoc-gen-grpc-web编译.proto文件,生成 TypeScript 桩代码。 - 客户端发出的请求由 Envoy 网关(或 Traefik)拦截。网关将 gRPC-Web 的自定义 Framing 格式翻译为标准 gRPC。
- 服务端返回的
Trailers被 Envoy 打包塞入响应体的末尾特殊数据块中,使浏览器端 JavaScript 能够完整读取错误码和状态。