grpc GateWay

grpc GateWay
安安gRPC 虽然性能强大,但它使用 HTTP/2 和 Protobuf(二进制) 协议。这导致了一个大问题:浏览器、Postman、前端 JavaScript 无法直接调用 gRPC 接口。
gRPC Gateway 就是为了解决这个问题而生的。
第一部分:什么是 gRPC Gateway?(通俗版)
想象你开了一家高档餐厅(gRPC 服务):
- 内部厨房:只说“专业术语”(Protobuf),只用“内部对讲机”(HTTP/2)。效率高,但外人听不懂。
- 外部顾客:说“大众语言”(JSON),用“手机点餐”(HTTP/1.1)。
gRPC Gateway 就是“服务员/翻译官”:
- 顾客发 HTTP/JSON 请求给 Gateway。
- Gateway 把 JSON 翻译成 Protobuf,把 HTTP 转成 gRPC。
- Gateway 发给 gRPC 服务。
- 拿到结果后,再翻译回 JSON 给顾客。
核心价值:
- 对外:提供标准的 RESTful HTTP/JSON 接口(兼容浏览器、移动端)。
- 对内:微服务之间依然用高效的 gRPC 通信。
- 一份代码:只需写一次
.proto文件,同时生成 gRPC 代码和 HTTP 接口代码。 - 自动生成文档:可以自动生成 Swagger/OpenAPI 文档。
第二部分:环境准备(关键步骤)
gRPC Gateway 需要额外的插件。请确保完成以下安装。
1. 安装插件
1 | # 1. 安装 gateway 插件 (生成反向代理代码) |
3. 获取 Google API 定义
Gateway 依赖 google/api/annotations.proto。你需要确保 protoc 能找到它。
最简单的方法是下载 googleapis 仓库:
1 | # 在任意目录克隆 ( protoc 需要包含这个路径) |
4. 初始化项目
1 | mkdir grpc-gateway-demo |
第三部分:定义 Proto 文件(核心变化)
这是与普通 gRPC 最大的不同。我们需要在 .proto 文件中添加 HTTP 映射注解。
文件路径: proto/gateway.proto
1 | syntax = "proto3"; |
生成代码
运行以下命令(注意 I 参数指向 googleapis 路径):
1 | # 假设 googleapis 在当前目录的上一级 |
生成的文件:
gateway.pb.go(消息定义)gateway_grpc.pb.go(gRPC 服务定义)gateway.pb.gw.go(Gateway 反向代理代码,重点!)gateway.swagger.json(Swagger 文档)
第四部分:完整 Demo 演示
我们将采用 单进程双端口 模式:
- **gRPC 端口 (50051)**:供内部微服务调用。
- **HTTP 端口 (8080)**:供 Gateway 监听,对外提供 REST 接口。
1. 实现 gRPC 业务逻辑 (server.go)
1 | package main |
2. 启动 gRPC 和 Gateway 服务 (main.go)
这是最关键的部分,将两者串联起来。
1 | package main |
第五部分:测试与验证
启动程序后 (go run main.go),你可以用两种方式测试。
1. 使用 Curl 测试 HTTP 接口
Gateway 已经帮你把 HTTP 转成了 gRPC。
测试 GetUser (GET 请求)
1
2curl http://localhost:8080/v1/users/1
# 输出:{"id":"1","name":"Alice","age":25}测试 CreateUser (POST 请求)
1
2
3
4curl -X POST http://localhost:8080/v1/users \
-H "Content-Type: application/json" \
-d '{"name": "Bob", "age": 30}'
# 输出:{"id":"100","name":"Bob","age":30}
2. 查看 Swagger UI
在浏览器打开 http://localhost:8080/swagger/ui/ (需要额外配置 swagger 服务,或者直接用 protoc 生成的 json 导入 Postman)。
注:为了简化 Demo,上面代码未直接挂载 swagger UI 文件,但生成了 gateway.swagger.json。你可以使用在线 Swagger Editor 导入该 JSON 查看文档。
第六部分:进阶技巧 (Proto 注解详解)
在 .proto 中,option (google.api.http) 非常灵活。
1. 路径参数 (Path Params)
1 | // URL: /v1/users/123 |
2. 查询参数 (Query Params)
如果 URL 路径中没有定义的字段,会自动变成 Query 参数。
1 | message ListUsersRequest { |
3. 请求体映射 (Body Mapping)
1 | // body: "*" 表示整个 JSON Body 映射到请求消息 |
4. 忽略某个字段
如果你不想让某个字段通过 HTTP 暴露:
1 | message Request { |
第七部分:常见问题与最佳实践
1. 跨域问题 (CORS)
浏览器直接调用 Gateway (8080) 通常会报 CORS 错误。你需要在 HTTP 服务器层处理。
解决方案:使用 rs/cors 中间件包裹 mux。
1 | import "github.com/rs/cors" |
2. 错误码映射
gRPC 的错误码(如 NotFound)会自动映射为 HTTP 状态码(如 404)。
OK->200InvalidArgument->400Unauthenticated->401NotFound->404Internal->500- 如果映射不符合预期,可以在 Gateway 配置中自定义
WithForwardResponseOption。
3. 性能损耗
Gateway 增加了 HTTP->gRPC->HTTP 的转换过程,会有轻微的性能损耗(序列化/反序列化)。
- 建议:内部微服务之间调用直接用 gRPC (50051),不要走 Gateway (8080)。Gateway 仅用于对外暴露。
4. 流式支持
早期的 gRPC Gateway 不支持流式 RPC。v2 版本支持了部分流式,但配置较复杂。
- 建议:如果需要 WebSocket 或 SSE,建议单独实现,或者使用 gRPC-Web。
第八部分:总结归纳表
| 组件/概念 | 作用 | 关键配置/命令 |
|---|---|---|
| protoc-gen-grpc-gateway | 生成反向代理 Go 代码 | go install ...@latest |
| google/api/annotations.proto | 定义 HTTP 映射规则 | 需引入 googleapis 仓库 |
| option (google.api.http) | Proto 中的核心注解 | 定义 get, post, body |
| runtime.ServeMux | Gateway 的路由处理器 | mux := runtime.NewServeMux() |
| RegisterXxxHandlerFromEndpoint | 将 gRPC 服务注册到 Gateway | 指向 gRPC 地址 localhost:50051 |
| protoc-gen-openapiv2 | 生成 Swagger 文档 | 生成 .swagger.json |
| CORS | 解决浏览器跨域 | 使用 rs/cors 中间件 |
| 错误映射 | gRPC Code -> HTTP Status | 自动映射,可自定义 |
学习路线图
- 跑通 Demo:先确保上面的
main.go能跑起来,Curl 能通。 - 理解注解:尝试修改
.proto中的get: "/v1/users/{id}",观察 URL 变化。 - 整合拦截器:之前的 gRPC 拦截器(认证、日志)在 Gateway 模式下依然有效,因为 Gateway 最终调用的是 gRPC 方法。你可以在 gRPC 层统一做鉴权。
- 生产部署:通常架构是
Nginx -> Gateway (HTTP) -> gRPC Service。

