Kratos 通过 protoc-gen-go-http 和 protoc-gen-go-grpc 插件实现 HTTP 与 gRPC 共存,需在 proto 中添加 google.api.http 注解并正确配置路径、参数映射及双 server 注册。

Kratos 不需要“桥接”或“网关”就能让 HTTP 与 gRPC 接口共存——它靠 protoc-gen-go-http 和 protoc-gen-go-grpc 两个代码生成插件,在启动时就注册两套独立但语义一致的 handler,本质是“一份 proto,双份服务端逻辑”。
proto 文件里必须加 google.api.http 注解
没这行注解,protoc-gen-go-http 就不会生成 HTTP 路由和绑定逻辑,HTTP server 启动后也收不到任何请求。
常见错误现象:curl http://localhost:8000/helloworld/kratos 返回 404,但 grpcurl -plaintext localhost:9000 list 能看到服务 —— 说明 gRPC 已生效,HTTP 部分根本没注册。
- 确保
import "google/api/annotations.proto";已声明,且该文件在protoc的--proto_path路径下(Kratos 模板默认已包含) -
get:、post:必须写在rpc方法的option块内,不能写在 message 或 service 外层 - 路径参数如
{name}对应 proto 中字段名(小写 snake_case),不是 Go 字段名(Name) - 如果只想要 gRPC、不要 HTTP,删掉
google.api.http注解即可,不影响 gRPC 生成
启动时要同时注册 HTTP 和 gRPC server
Kratos 的 cmd/xxx/main.go 里默认调用 app.Run(),它会遍历 internal/server/ 下所有 New* 函数。但如果你删了某个 server 的初始化,或者忘了在 server.NewHTTPServer 里传入生成的 http.Handler,HTTP 接口就彻底消失。
- 检查
internal/server/http.go:确认h := &http.Server{Addr: addr, Handler: srv}中的srv是api.RegisterGreeterHTTPServer生成的 handler,不是空 struct - 检查
internal/server/grpc.go:确认grpc.NewServer()后调用了api.RegisterGreeterServer(srv, service) - 两个 server 的监听地址必须不同(如
:8000和:9000),否则端口冲突直接 panic - HTTP server 默认不启用 CORS、gzip 等中间件,需手动在
http.Server.Handler外再包一层
protoc 命令要同时跑两个插件
只跑 protoc-gen-go-grpc,你会得到 gRPC client/server;只跑 protoc-gen-go-http,你只会得到 HTTP handler,没有 gRPC stub。两者缺一不可。
典型命令(在项目根目录执行):
protoc --go_out=. --go-grpc_out=. --go-http_out=. \ -I . \ -I $(go env GOPATH)/pkg/mod/github.com/go-kratos/kratos/v2@v2.11.0/third_party \ api/helloworld/v1/helloworld.proto
-
--go-http_out=.是关键,漏掉就无 HTTP 代码 -
-I必须包含 Kratos 提供的第三方 proto 路径(如google/api/annotations.proto),否则报File not found - 生成的 HTTP handler 文件默认叫
helloworld_http.pb.go,gRPC 是helloworld_grpc.pb.go,别名冲突会导致编译失败 - 修改 proto 后必须重新运行
protoc,Kratos 不支持热重载 proto 定义
调试时注意请求路径和 body 格式差异
同一个 RPC 方法映射成 HTTP 后,路径、参数提取、body 解析规则全由 google.api.http 控制,和 gRPC 的二进制 wire format 完全不同。
- GET 请求路径参数(如
/helloworld/{name})会被自动提取并赋值给HelloRequest.Name,无需 JSON body - POST 请求若设
body: "*",整个 JSON body 会反序列化进HelloRequest;若设body: "name",则只取 JSON 中name字段赋值 - HTTP 接口返回的是 JSON,默认开启
Content-Type: application/json;gRPC 返回 Protobuf 二进制流,客户端必须用对应语言的 stub 解析 - HTTP 错误码(如 400)来自 binding 验证失败,而 gRPC 错误码(
codes.InvalidArgument)来自服务端status.Error,两者语义不等价,前端需分别处理
最易被忽略的是:HTTP 接口的 URL 路径和 query 参数解析发生在框架层,早于业务 handler 执行;而 gRPC 全部参数都在 request message 里,没有路径概念。这意味着,你在 HTTP handler 里改 r.URL.Path 不会影响最终传给业务方法的 HelloRequest 结构体 —— 它早已被中间件解析好了。











