ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

gRPC-Gateway 实战:使用 protoc 从 .proto 文件生成 Go 类型与 gRPC Stub 代码

gRPC-Gateway 实战:使用 protoc 从 .proto 文件生成 Go 类型与 gRPC Stub 代码 gRPC-Gateway 实战使用 protoc 从 .proto 文件生成 Go 类型与 gRPC Stub 代码【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway本教程是 gRPC-Gateway 官方文档中「Generating stubs」系列的一部分围绕如何在仓库根目录下、以protoc命令行工具为入口从proto/helloworld/hello_world.proto生成 Go 所需的全部桩代码stub包括消息类型代码*.pb.go与 gRPC 服务定义代码*_grpc.pb.go。读完本文你将掌握protoc各核心参数-I、--go_out、--go-grpc_out、pathssource_relative等的确切含义与用法知道生成产物落在何处、每个文件承担什么职责并能顺藤摸瓜扩展出 gRPC-Gateway 的 HTTP 反向代理代码*.gw.pb.go。生成 Go Stub 的两条路线protoc 与 buf在开始动手之前先明确本教程在整个 gRPC-Gateway 教程体系中的位置。官方文档在 Generating stubs 一节中明确说明生成 stub 有两条可选路线——protoc与buf。protocProtocol Buffers 官方编译器是业界使用最广泛的经典生成方式但也存在较陡峭的学习曲线所有.proto文件都需要在命令行上手工指定各类*_out、*_opt参数繁多。buf较新的工具以用户体验和速度为设计目标额外提供 lint代码风格检查与 breaking change detection破坏性变更检测这是protoc不具备的能力。本文聚焦protoc路线想了解 buf 方案的读者可阅读姊妹篇 使用 buf 生成 stubs。值得提前说明的是gRPC-Gateway 仓库自身在 Makefile 的proto目标中统一使用buf generate驱动代码生成并以 buf.gen.yaml 作为模板但理解protoc的参数体系仍然是理解这一切生成机制的基础。前置准备protoc 与两个 Go 插件protoc本身只负责解析.proto文件具体产物的生成由「插件plugin」完成。插件以protoc-gen-name命名protoc会在PATH中查找它们。生成 Go 桩代码需要两个官方插件插件生成内容典型产物protoc-gen-go命令行参数--go_outGo 消息类型message编译成的 Go struct 及其序列化逻辑*.pb.goprotoc-gen-go-grpc命令行参数--go-grpc_outgRPC 服务定义客户端接口、服务端接口、注册函数、UnimplementedXxxServer骨架*_grpc.pb.go安装这两个插件的方式是go install具体版本可参考仓库 go.mod 中锁定的google.golang.org/protobuf与google.golang.org/grpc版本安装时选择与之兼容的插件版本即可$ go install google.golang.org/protobuf/cmd/protoc-gen-golatest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpclatest同时确保本机已安装protoc本体。如果你还需要生成 gRPC-Gateway 的 HTTP 代理代码还需安装仓库自带的protoc-gen-grpc-gateway见下文「扩展生成 gRPC-Gateway 反向代理代码」一节。编写一个可供生成的 .proto 文件在运行protoc之前需要先有一个.proto文件。官方教程 创建一个简单的 Hello World gRPC 服务 给出了最小示例在proto/helloworld/hello_world.proto中定义一个Greeter服务及其SayHelloRPC以及HelloRequest/HelloReply两个消息。syntax proto3; package helloworld; // The greeting service definition service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the users name message HelloRequest { string name 1; } // The response message containing the greetings message HelloReply { string message 1; }仓库中的真实示例 examples/internal/helloworld/helloworld.proto 更完整地展示了实际工程中会出现的两个关键点go_package选项通过option go_package github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/helloworld;声明生成的 Go 文件所属的 import 路径。protoc-gen-go生成代码时依赖它确定package声明与 import 路径这是 Go 工程中每个.proto文件都应当配置的选项。google.api.http注解与google.protobuf.wrappers等依赖import google/api/annotations.proto;是后续让 gRPC-Gateway 生成 HTTP 映射代码的前提一旦 proto 文件引入了外部依赖protoc的-I参数就必须覆盖到这些依赖所在的目录详见下文。核心命令protoc 生成 Go stub 的完整示例官方文档给出的protoc命令如下假设你正位于仓库根目录且.proto文件都放在名为proto的目录下$ protoc -I ./proto \ --go_out ./proto --go_opt pathssource_relative \ --go-grpc_out ./proto --go-grpc_opt pathssource_relative \ ./proto/helloworld/hello_world.proto这一条命令同时完成两件事用go插件生成 Go 消息类型用go-grpc插件生成 gRPC 服务定义。下面逐段拆解每个参数的实际作用。-I ./proto设置 import 搜索路径-I等价于--proto_path告诉protoc到哪些目录下查找.proto文件及其 import 的依赖。它有两个作用确定命令行末尾列出的.proto文件的解析入口为.proto文件内部的import语句提供搜索根。例如当你的 proto 引入google/api/annotations.proto时protoc会沿着-I指定的路径去找它。官方文档在 adding_annotations.md 中演示了完整的目录布局将googleapis中的annotations.proto与http.proto复制进本地 proto 结构后目录会呈现如下形态proto ├── google │ └── api │ ├── annotations.proto │ └── http.proto └── helloworld └── hello_world.proto此时只要-I ./protoprotoc就能解析import google/api/annotations.proto。--go_out与--go_opt pathssource_relative--go_out ./proto指定protoc-gen-go的输出目录。输出目录决定的是「顶层根目录」而生成文件具体落在哪个子路径则由paths模式决定——这正是--go_opt pathssource_relative的作用。protoc-gen-go支持两种paths取值import默认模式按go_package声明的 import 路径逐级创建目录并把文件写入其中。例如go_package github.com/myuser/myrepo/proto/helloworld时产物会出现在以输出目录为根、逐级嵌套的github.com/myuser/myrepo/proto/helloworld/下。这种模式适合生成代码与源码隔离放置的场景。source_relative忽略go_package的路径信息直接把生成文件写到与源.proto文件相同的目录下。这就是官方文档强调的「generated files will appear in the same directory as the source.protofile」。官方文档推荐的source_relative模式对初学者更直观运行命令后proto/helloworld/下会同时出现源文件和生成文件对照检查非常方便。--go-grpc_out与--go-grpc_opt pathssource_relativego-grpc插件protoc-gen-go-grpc的用法与go插件完全对称--go-grpc_out指定输出目录--go-grpc_opt pathssource_relative同样让生成文件落在源 proto 同目录。它将service Greeter { rpc SayHello(...) }编译为客户端接口GreeterClient及其默认实现greeterClient服务端接口GreeterServer与安全骨架UnimplementedGreeterServer方便你嵌入自己的实现而无需实现全部方法注册函数RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer)。官方教程 创建 main.go 中server结构体嵌入helloworldpb.UnimplementedGreeterServer、再调用helloworldpb.RegisterGreeterServer(s, server{})的写法依赖的正是go-grpc插件生成的这些类型与函数。生成产物*.pb.go与*_grpc.pb.go命令运行完毕后proto/helloworld/hello_world.proto会对应生成两个文件proto/helloworld/ ├── hello_world.proto # 源文件 ├── hello_world.pb.go # 由 go 插件生成HelloRequest / HelloReply 等消息类型 └── hello_world_grpc.pb.go # 由 go-grpc 插件生成Greeter 服务的客户端与服务端定义这一「一个源文件、两个产物」的规律可以直接在仓库中得到验证。仓库中examples/internal/helloworld/目录存放了真实生成好的三件套examples/internal/helloworld/helloworld.proto源文件含google.api.http注解examples/internal/helloworld/helloworld.pb.goHelloRequest/HelloReply消息类型代码examples/internal/helloworld/helloworld_grpc.pb.goGreeter服务定义代码。另外仓库中该目录还有helloworld.pb.gw.go与helloworld.swagger.json、helloworld.openapi.json它们分别由 gRPC-Gateway 与 OpenAPI 生成器产出见下一节正好构成了一个完整示例应包含的全部生成物。扩展追加 grpc-gateway 插件生成 HTTP 反向代理代码只生成 Go 类型与 gRPC 服务定义还不足以让 gRPC-Gateway 工作。要让网关把 HTTP/JSON 请求转发到 gRPC 服务还需要protoc-gen-grpc-gateway插件生成*.gw.pb.go文件。官方教程 adding_annotations.md 给出了在protoc命令中追加该插件的完整写法$ protoc -I ./proto \ --go_out ./proto --go_opt pathssource_relative \ --go-grpc_out ./proto --go-grpc_opt pathssource_relative \ --grpc-gateway_out ./proto --grpc-gateway_opt pathssource_relative \ ./proto/helloworld/hello_world.proto前提有两个proto 文件中的 RPC 方法带有google.api.http注解例如import google/api/annotations.proto; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) { post: /v1/example/echo body: * }; } }本地 proto 结构中包含google/api/annotations.proto与google/api/http.proto即上文的目录布局且-I能搜索到它们。运行后protoc-gen-grpc-gateway会额外生成*.gw.pb.go。仓库中 examples/internal/helloworld/helloworld.pb.gw.go 即是此类产物的实例其中包含RegisterGreeterHandler等函数供 runtime.NewServeMux() 注册使用——这正是 adding_annotations.md 中main.go示例里helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn)一行背后的代码来源。protoc-gen-grpc-gateway的完整实现位于 protoc-gen-grpc-gateway/internal/gengateway对生成逻辑感兴趣的读者可以深入阅读其 generator.go。仓库实践佐证从 Makefile 与 buf 配置反推 protoc 参数语义虽然 gRPC-Gateway 仓库自身使用 buf 驱动生成见 buf.gen.yaml其中以pathssource_relative为go、go-grpc、grpc-gateway、openapiv2四个插件统一配置输出模式但 Makefile 的proto目标可以反证本教程讲解的参数体系在实际工程中的对应关系生成 Go 类型与服务定义buf generate使用buf.build/protocolbuffers/go与buf.build/grpc/go两个插件等价于protoc的--go_out与--go-grpc_out生成网关代码local: protoc-gen-grpc-gateway插件等价于protoc的--grpc-gateway_out生成 OpenAPI 文档local: protoc-gen-openapiv2插件产出*.swagger.json仓库中如 examples/internal/proto/examplepb/a_bit_of_everything.swagger.json全局的pathssource_relative选项与protoc路线完全一致说明无论走哪条路线推荐的输出布局是统一的。仓库根目录 go.mod 的模块名为github.com/grpc-ecosystem/grpc-gateway/v2这解释了 examples/internal/helloworld/helloworld.proto 中go_package为何以该路径开头——go_package必须与最终放置生成文件的 Go module 内的路径保持一致才能被正常 import。常见问题与注意事项生成的包无法 import检查两处go_package选项是否与文件在 module 内的实际路径一致生成文件是否真的落在go_package对应目录若使用默认pathsimport模式文件会按go_package嵌套输出务必确认 import 路径与磁盘路径匹配。--go_opt与--go-grpc_opt必须分别指定两个插件各自独立维护paths等选项只写--go_opt pathssource_relative不会作用于go-grpc插件。proto 引入了外部依赖却解析失败将依赖文件放入-I覆盖的某个目录如上文proto/google/api/布局并确认命令行末尾仅列出待编译的源文件、而非依赖文件。想统一管理生成流程当 proto 文件数量增多后逐一手写protoc命令会变得冗长易错这正是官方推荐迁移到 buf使用 buf 生成 stubs的原因buf 会递归发现配置目录下的所有.proto文件并自动构建省去手工罗列。下一步生成完*.pb.go与*_grpc.pb.go之后教程的下一个环节是编写 main.go 启动 gRPC 服务将Greeter注册到grpc.NewServer()上运行随后在 adding_annotations.md 中为 proto 添加google.api.http注解并生成*.gw.pb.go最终在同一个进程中同时监听 gRPC 端口:8080与 HTTP 网关端口:8090用curl验证 JSON 请求到 gRPC 调用的完整链路。【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表