使用 buf 生成 gRPC 网关 Stub:grpc-gateway 项目的代码生成实践指南
【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway
本文以 grpc-gateway 项目官方教程《Generating stubs using buf》为主线,完整讲解如何用 buf 替代 protoc 递归发现.proto文件、通过buf.yaml与buf.gen.yaml配置 Go 类型与 gRPC 服务 Stub 的生成,并对照仓库根目录的真实配置与 Makefile 中的调用方式,帮助读者一次性掌握从安装、配置到产出*.pb.go/*_grpc.pb.go的完整链路,为后续接入 grpc-gateway 网关代码生成打下基础。
为什么选择 buf 而非 protoc
在 grpc-gateway 教程体系中,生成 Stub 有两条路线:protoc路线与buf路线,两者的定位在生成 Stub 总览中有清晰说明:protoc是业界使用最广泛的经典生成工具,但学习曲线较陡;而buf是更年轻、以用户体验和速度为导向的工具,并且额外提供 lint(代码风格检查)与 breaking change detection(破坏性变更检测)——这两项能力是protoc本身不具备的。
buf 提供的 Protobuf 工具链能力包括:
- lint:对
.proto文件的命名规范、字段风格等进行静态检查; - breaking change detection:对比新旧版本,检测可能破坏二进制兼容性的改动;
- generation:基于插件机制批量生成类型、服务与网关代码。
安装 buf
grpc-gateway 仓库自身通过 Go 工具链安装 buf,在 Makefile 中固定了版本:
go install github.com/bufbuild/buf/cmd/buf@v1.45.0该命令将 buf 安装到$GOBIN(默认$GOPATH/bin),确保后续buf generate等子命令可用。除此之外,buf 官方也提供多种系统安装方式,具体可查阅其官方安装文档(当前仓库教程即指向该文档)。
用 buf.yaml 声明 Protobuf 模块
buf 与protoc最直观的差异在于输入文件的管理方式:protoc需要把每个.proto文件显式罗列在命令行上(参见 使用 protoc 生成 Stub 中的示例),而 buf 会在配置指定的文件层级下递归发现所有.proto文件并统一构建,无需逐个列举。
这个递归发现行为由buf.yaml控制。buf.yaml应检入(check in)到 Protobuf 文件层级(file hierarchy)的根目录,buf 会在运行时自动读取它(只要该文件存在)。此外,配置也可以通过命令行参数--config提供,它接受:
- 指向
.json或.yaml文件的路径; - 或直接内联的 JSON / YAML 配置数据。
原教程给出的最小合法配置如下,将其放在 Protobuf 文件层级的根目录,例如仓库根目录下的proto/buf.yaml:
version: v1 name: buf.build/myuser/myrepoversion:配置文件版本,目前为v1;name:模块的规范名称(Buf Schema Registry 风格),用于标识你的 Protobuf 模块。
仓库真实的 buf.yaml:不止两行
grpc-gateway 仓库根目录的 buf.yaml 是这一配置在实际大型项目中的完整形态,它展示了version/name之外的更多能力:
version: v1 name: buf.build/grpc-ecosystem/grpc-gateway deps: - buf.build/googleapis/googleapis breaking: use: - FILE lint: use: - DEFAULT ignore_only: DIRECTORY_SAME_PACKAGE: - examples/internal/proto/examplepb/a_bit_of_everything.proto # ... 其他按规则分组的忽略清单 allow_comment_ignores: true build: excludes: - bazel-grpc-gateway对照教程要点,可逐项理解:
deps:声明外部依赖模块。这里的buf.build/googleapis/googleapis会被解析并写入 buf.lock(该文件为自动生成、勿手改,其中记录了远程模块的 owner、repository 与 commit 哈希,用于锁定版本);breaking.use: [FILE]:以文件为单位检测破坏性变更;lint.use: [DEFAULT]:启用默认 lint 规则集,并通过ignore_only对ENUM_VALUE_PREFIX、FIELD_LOWER_SNAKE_CASE、PACKAGE_DIRECTORY_MATCH等具体规则逐文件豁免——这正体现了教程所说的 lint 能力,也解释了为何仓库中允许存在部分不完全符合默认风格的历史 proto;allow_comment_ignores: true:允许在.proto源码中用注释屏蔽 lint 规则;build.excludes:构建时排除bazel-grpc-gateway目录,避免误把 Bazel 生成的目录纳入模块。
用 buf.gen.yaml 定义生成模板
配置好模块后,还需要一个生成模板文件buf.gen.yaml来告诉 buf:调用哪些插件、输出到哪里、传什么参数。原教程为 Go 类型与 gRPC Stub 给出的模板如下:
version: v1 plugins: - plugin: go out: proto opt: paths=source_relative - plugin: go-grpc out: proto opt: paths=source_relativeplugin: go/plugin: go-grpc:分别调用 Go 类型生成插件与 gRPC 服务定义生成插件,产出*.pb.go与*_grpc.pb.go;out: proto:生成文件相对于proto目录输出;opt: paths=source_relative:生成的 Go 文件与源.proto文件位于同一目录,避免按 Go import path 重建目录树。
仓库中的 v2 模板语法与 grpc-gateway 插件
grpc-gateway 根目录的 buf.gen.yaml 是教程示例的升级形态,使用了version: v2语法,并加入了 grpc-gateway 自己的两个生成插件:
version: v2 plugins: - remote: buf.build/protocolbuffers/go:v1.35.1 out: . opt: - paths=source_relative - remote: buf.build/grpc/go:v1.5.1 out: . opt: - paths=source_relative - require_unimplemented_servers=false - local: protoc-gen-grpc-gateway out: . opt: - paths=source_relative - allow_repeated_fields_in_body=true - local: protoc-gen-openapiv2 out: . opt: - allow_repeated_fields_in_body=true与教程的 v1 示例相比,v2 语法的差异要点:
- 插件来源字段:v1 用
plugin: go表示本地插件;v2 区分为remote:(从 Buf Schema Registry 拉取远程插件,如buf.build/protocolbuffers/go:v1.35.1)与local:(调用本地已安装的可执行文件,如仓库自带的protoc-gen-grpc-gateway、protoc-gen-openapiv2); - opt 列表化:v1 的
opt是单个字符串,v2 中为字符串列表,可一次传多个参数(如paths=source_relative与require_unimplemented_servers=false并列); - 网关插件:
protoc-gen-grpc-gateway生成*.pb.gw.go反向代理桩代码,protoc-gen-openapiv2生成 OpenAPI v2 描述文档——这正是 grpc-gateway 项目把“类型 Stub → gRPC Stub → HTTP 网关桩 → OpenAPI 文档”一次性串联起来的关键。
仓库内还有一批面向特定场景的模板文件,进一步展示了opt参数的定制能力:
- enum_with_single_value.buf.gen.yaml:为
protoc-gen-openapiv2传入omit_enum_default_value=true,控制单值枚举在 OpenAPI 输出中的呈现; - protoc-gen-openapiv2/options/buf.gen.yaml:为 Go 插件传入
default_api_level=API_HYBRID等选项; - protoc-gen-openapiv3/buf.gen.yaml:使用本地
protoc-gen-openapiv3插件产出 OpenAPI v3 描述。
运行 buf generate 并检查产物
配置完成后,在 Protobuf 文件层级根目录执行:
$ buf generatebuf 会读取根目录的buf.gen.yaml(也可用--template显式指定其他模板文件),递归构建模块内所有.proto文件,并为每个 protobuf package 生成:
*.pb.go:Go 类型定义;*_grpc.pb.go:gRPC 服务接口与客户端/服务端桩。
在 grpc-gateway 仓库中,可以直观看到产物与源文件同目录落盘:例如 helloworld.proto 旁边就躺着 helloworld.pb.go、helloworld_grpc.pb.go 以及网关桩 helloworld.pb.gw.go 和 helloworld.swagger.json——这正是paths=source_relative的落地效果。
多模板并存的真实用法:--template
一个实际项目往往需要多套生成配置。grpc-gateway 的 Makefile 展示了这一实践:默认执行buf generate,随后用--template反复调用不同模板生成额外产物,例如:
buf generate --template ./examples/internal/proto/examplepb/openapi_merge.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/standalone_echo_service.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/unannotated_echo_service.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/generate_unbound_methods.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/use_go_template.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/ignore_comment.buf.gen.yaml buf generate --template ./examples/internal/proto/examplepb/remove_internal_comment.buf.gen.yaml # 以及多组 visibility 规则模板、enum_with_single_value、proto3_field_semantics、 # opaque、protoc-gen-openapiv2/options、protoc-gen-openapiv3/options 等由此可以看出,--template参数让“同一份 proto 源码、多份定制输出”成为可能——这正是教程中--config之外、buf 命令行灵活性的延伸,也是把 lint、breaking、多插件输出统一进 CI 流水线的基础。
依赖锁定与版本管理:buf.lock
当buf.yaml声明了deps后,首次运行相关命令会生成 buf.lock。该文件由 buf 自动维护并应检入版本库,其内容锁定远程模块的确切提交,保证团队成员生成结果一致:
# Generated by buf. DO NOT EDIT. version: v1 deps: - remote: buf.build owner: googleapis repository: googleapis commit: 62f35d8aed1149c291d606d958a7ce32在 grpc-gateway 中,googleapis 依赖为*.proto中的google.api.http等注解提供了类型来源——这是 grpc-gateway 路由映射(HTTP annotation)得以编译的基础。结合 go.mod 中的 Go 模块依赖,buf 生态的模块锁定(buf.lock)与 Go 生态的模块锁定(go.sum)共同构成了完整的可复现构建链路。
从 Stub 到网关:完整的接入路径
生成 Stub 只是 grpc-gateway 开发流程的第一步。教程的下一步指向 creating_main.go,即编写入口代码把 gRPC 服务与 HTTP 网关装配起来。若要从零走通全流程,建议按以下顺序阅读本教程系列:
- 教程导览与简单 Hello World 示例建立整体认知;
- 在
.proto中加入 google.api.http 注解,声明 HTTP 路由映射; - 按本文方式用 buf(或 protoc)生成类型与 gRPC Stub,再叠加 grpc-gateway 与 openapiv2 插件生成网关桩与 API 文档;
- 参考 creating_main.go 组装服务端与网关,最终落地运行。
小结
buf 通过buf.yaml(模块声明、lint 与 breaking 规则、依赖)+buf.gen.yaml(插件、输出路径、插件参数)取代了 protoc 繁琐的命令行文件罗列,并借助递归发现、远程插件与--template多模板机制,让 Stub 生成变得可声明、可复用、可锁定。grpc-gateway 仓库本身既是这套工具链的深度用户,也是最佳参考样例——从根目录的 buf.yaml、buf.gen.yaml、buf.lock 到 Makefile 中的全套生成指令,都可以直接对照本文的每一步进行验证与扩展。
【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考