grpc-protobuf-build 完整使用指南:用 build.rs 编译 proto 并为 grpc/tonic 生成服务桩代码
后端RPC框架【免费下载链接】grpc-rustA native gRPC client server implementation with async/await support.项目地址https://gitcode.com/GitHub_Trending/to/grpc-rust点击查看免费下载grpc-protobuf-build 是本仓库grpc-rust提供的 protobuf 构建集成库它把protoc编译器与protoc-gen-rust-grpc插件封装进 Rust 的build.rs构建脚本体系一键完成proto 消息代码 gRPC 服务桩代码的双份生成产物可直接供grpc原生实现与tonictower 实现两套运行时使用。读完本文你将掌握它的依赖配置、默认与深度自定义两种构建写法、生成代码的三种引用方式以及protoc/插件二进制的定位优先级与源码级工作流程可直接落地到自己的 Rust 项目。一、定位与边界它解决什么问题在 gRPC Rust 生态中开发者通常需要把.proto接口定义翻译成两类 Rust 代码消息代码message codemessage HelloRequest {...}对应的结构体与序列化实现服务代码service codeservice Greeter {...}对应的客户端Client/ 服务端Servertrait 桩。grpc-protobuf-build 将这两步统一收敛到一个build.rs中完成。按 grpc-protobuf-build/README.md 的官方描述它通过 protobuf rust 编译 proto 文件并生成供 tonic 使用的服务桩与 proto 定义。需要特别注意的是版本边界当前grpc-protobuf-build属于preview预览版本官方明确声明不建议用于生产环境所有 API 均不稳定自行承担风险。从 grpc-protobuf-build/Cargo.toml 可以看到其当前版本号为0.10.0Edition 为 2024作者为 gRPC authorsLicense 为 MIT。因此本文介绍的内容在后续大版本中可能调整使用时请以实际安装的 crate 版本为准。二、工作原理与产物形态grpc-protobuf-build的工作方式是作为项目根目录的build.rs构建脚本 的文档注释。其内部核心流程见 grpc-protobuf-build/src/lib.rs 的compile()实现分两步生成消息代码调用protobuf_codegen::CodeGenprotobuf rust 官方 codegen产物为generated.rs汇总入口与{proto文件名}.u.pb.rs各消息结构体。生成服务代码调用protoc --pluginprotoc-gen-rust-grpc...以--rust-grpc_out指定输出目录产物为{proto文件名}_grpc.pb.rs。生成文件的命名与目录规则代码会落在output_dir下与 proto 文件路径对应的子目录中grpc-protobuf-build/src/lib.rs。本仓库中已生成的实例如下可作对照examples/generated/helloworld/generated.rs汇总入口通过#[path helloworld.u.pb.rs]引入消息模块并在__unstable模块中内嵌HELLOWORLD_DESCRIPTOR_INFO描述符信息examples/generated/helloworld/helloworld.u.pb.rsHelloRequest/HelloReply等消息结构体examples/generated/helloworld/helloworld_grpc.pb.rsGreeter服务的客户端/服务端桩代码examples/generated/routeguide/generated.rs、examples/generated/routeguide/route_guide.u.pb.rs、examples/generated/routeguide/route_guide_grpc.pb.rsRouteGuide 示例的对应产物。如果某个 proto 文件中没有 service 定义则对应的_grpc.pb.rs不会被生成源码注释明确说明该文件可能不存在见 grpc-protobuf-build/src/lib.rs。三、环境准备protoc 与插件二进制从哪来构建时compile()首先会解析protoc与protoc-gen-rust-grpc两个二进制的位置resolve_binaries()见 grpc-protobuf-build/src/lib.rs按以下优先级依次尝试显式指定通过CodeGen::prebuilt_binaries(protoc, plugin)传入两个二进制路径源码编译启用protoc-gen-rust-grpcfeature 时使用protoc_gen_rust_grpc::protoc()与protoc_gen_rust_grpc::protoc_gen_rust_grpc()编译产物若 C 构建被跳过则文件可能不存在环境变量GRPC_RUST_PROTOC_DIR指向的目录其中需同时包含protoc与protoc-gen-rust-grpcWindows 下为.exe后缀系统 PATH通过which在 PATH 中查找两个二进制。全部失败时构建会报错并提示四种解决方案启用 build-plugin feature / 设置环境变量 / 加入 PATH / 在 build.rs 中显式传路径。resolve_binaries()还会对每个二进制执行--version可运行性检查grpc-protobuf-build/src/lib.rs。对应地examples/README.md 给出了安装protoc的常用命令# Ubuntu sudo apt update sudo apt upgrade -y sudo apt install -y protobuf-compiler libprotobuf-dev # Alpine Linux sudo apk add protoc protobuf-dev # macOSHomebrew brew install protobufprotoc-gen-rust-grpc插件二进制可以从仓库 releases 下载放入 PATH或直接启用build-pluginfeature 让构建期从 C 源码编译需要 CMake 与 C17 编译器具体编译步骤见 protoc-gen-rust-grpc/README.md 的cmake -B build -DCMAKE_BUILD_TYPERelease流程。本仓库中examples、grpc-protobuf、interop等 crate 均以default-features false引入本库见 examples/Cargo.toml、grpc-protobuf/Cargo.toml、interop/Cargo.toml即默认不触发 C 编译改从外部寻找二进制。四、依赖配置Features在项目的Cargo.toml中官方 README 给出的最小依赖配置为[dependencies] protobuf protobuf-version [build-dependencies] grpc-protobuf-build grpc-version其中protobuf消息运行库与protobuf-codegen构建期 codegen应保持版本匹配——本仓库当前使用protobuf-codegen 4.35.1-release见 grpc-protobuf-build/Cargo.toml可作为版本对齐的参考。本库自身还提供两个 feature见 grpc-protobuf-build/Cargo.toml[features] default [build-plugin] build-plugin [protoc-gen-rust-grpc] # 旧名别名兼容既有使用者default默认启用携带可选的protoc-gen-rust-grpc依赖构建时尝试用其自带的protoc与插件build-plugin是protoc-gen-rust-grpcfeature 的别名供老用户平滑升级。五、快速上手默认配置一行编译官方推荐的入门写法是依靠默认值在项目根目录创建build.rsfn main() - Result(), Boxdyn std::error::Error { grpc_protobuf_build::CodeGen::new() .include(proto) .inputs([service.proto]) .compile()?; Ok(()) }各环节的默认行为如下对应 grpc-protobuf-build/src/lib.rs 的CodeGen::new()输出目录默认取环境变量OUT_DIRCargo 为每次构建提供的临时目录消息代码默认生成generate_message_code默认true代码格式化默认开启should_format_code默认true使用prettyplease重新排版生成代码客户端模式默认关闭client_only默认false即同时生成客户端与服务端桩。include(proto)等价于protoc的--proto_path为 proto 的 import 解析提供搜索根目录inputs([service.proto])指定要编译的 proto 文件。同一目录下多文件时可调用inputs([...])一次传入数组或多次调用input(...)逐个添加。六、深度自定义Dependency 与 CodeGen 全参数当项目结构复杂——例如消息代码由外部 crate 提供、proto 分散在多个 include 目录、或消息与服务桩需要分属不同模块时官方 README 给出如下完整示例fn main() - Result(), Boxdyn std::error::Error { let dependency grpc_protobuf_build::Dependency::builder() .crate_name(external_protos.to_string()) .proto_import_paths(vec![PathBuf::from(external/message.proto)]) .proto_files(vec![message.proto.to_string()]) .build()?; grpc_protobuf_build::CodeGen::new() .generate_message_code(false) .inputs([proto/helloworld/helloworld.proto]) .include(external) .message_module_path(super::proto) .dependencies(vec![dependency]) //.output_dir(src/generated) // 可修改生成代码的存放位置 .compile()?; Ok(()) }6.1 Dependency描述外部 crate 中的 proto 符号Dependency用于声明当前编译的 proto 文件所引用的、但消息代码已在另一个 crate 中生成的符号来源定义见 grpc-protobuf-build/src/lib.rs。其字段与构建期作用构建器方法字段类型作用crate_name(name)String外部 crate 名称必填build()缺失时会返回错误crate_name is requiredproto_import_path(path)/proto_import_paths(paths)VecPathBuf该 crate 中 proto 文件的路径列表既用于构建期rerun-if-changed监听文件变化触发重建也作为--proto_path传入 protocproto_file(file)/proto_files(files)VecString该 crate 中已完成 codegen 的 proto 文件名称列表Dependency与protobuf_codegen::Dependency实现了双向From转换grpc-protobuf-build/src/lib.rs因此既能接收protobuf_well_known_types::get_dependency(...)等外部工具生成的依赖如本仓库 grpc-benchmark/build.rs 中 well-known types 的用法也能转回标准 codegen 依赖。6.2 CodeGen 全方法速查结合源码grpc-protobuf-build/src/lib.rs整理常用配置方法方法默认值说明input(input)/inputs([...])空添加要编译的 proto 文件相对include目录的路径include(dir)/includes([...])空添加 proto import 搜索根目录对应--proto_pathoutput_dir(dir)$OUT_DIR生成代码的输出目录生成文件位于其中与 proto 路径对应的子目录generate_message_code(bool)true是否生成消息代码为false时消息代码由外部独立生成仅生成服务桩message_module_path(path)self服务代码引用消息结构体时使用的 Rust 模块路径消息与服务桩分属不同模块时必设dependencies(vec![...])空注入外部 proto 依赖见 6.1client_only()false置为true时仅生成客户端代码等价于传--rust-grpc_optclient_onlytrueshould_format_code(bool)true是否用 prettyplease 格式化生成的 Rust 代码prebuilt_binaries(protoc, plugin)无显式指定protoc与protoc-gen-rust-grpc两个二进制路径6.3 message_module_path 与 crate_mapping 的底层传递服务代码生成时compile()会构造如下 protoc 调用grpc-protobuf-build/src/lib.rs--pluginprotoc-gen-rust-grpcplugin路径注册插件--rust-grpc_outoutput_dir服务代码输出目录--rust-grpc_optcrate_mapping路径传递crate_mapping.txt的路径--rust-grpc_optmessage_module_path路径仅在设置了message_module_path时传递插件侧该选项默认值为self参见 protoc-gen-rust-grpc/README.md--rust-grpc_optclient_onlytrue仅当启用client_only()每个include与每个依赖的proto_import_paths都会追加--proto_path...。crate_mapping.txt是插件在多 crate 场景下解析符号归属的中间文件若消息代码由本构建生成它直接复用消息 codegen 产出的映射文件若generate_message_code(false)则由generate_crate_mapping_file()按crate_name → proto 文件数量 → 逐行 proto 文件名的格式现场写出grpc-protobuf-build/src/lib.rs编译结束后自动删除。6.4 生成代码的格式化默认开启的format_code()grpc-protobuf-build/src/lib.rs会枚举generated.rs、各输入的{stem}_grpc.pb.rs与{stem}.u.pb.rs用syn解析语法树后再由prettyplease重新输出规范排版。这一设计让生成代码与手写代码共享统一的 rustfmt 风格避免把未经排版的生成物直接塞进仓库。七、三种方式引用生成的代码方式一默认 OUT_DIR include! 宏不修改output_dir时代码落在$OUT_DIR可在库/二进制中这样引入mod protos { // 引入消息代码。 include!(concat!(env!(OUT_DIR), proto/helloworld/generated.rs)); } mod grpc { // 引入服务代码。 include!(concat!(env!(OUT_DIR), proto/helloworld/helloworld_grpc.pb.rs)); }generated.rs作为汇总入口会以#[path]方式连带引入helloworld.u.pb.rs对照 examples/generated/helloworld/generated.rs 的实际形态因此只需include!它即可获得全部消息类型。方式二include_proto! 宏简化导入若未修改message_module_path保持默认self即消息与服务代码期望位于同一模块可直接用grpccrate 提供的include_proto!宏一步完成pub mod grpc_pb { grpc::include_proto!(proto/helloworld, helloworld); }方式三output_dir 落盘 仓库内模块若希望把生成代码固化进自己的代码库例如像本仓库examples/generated那样检入仓库、避免每个构建环境都装 protoc取消.output_dir(...)一行的注释然后在库文件中手动组织模块pub mod generated { pub mod helloworld { pub mod proto { include!(helloworld/generated.rs); } pub mod grpc { include!(helloworld/test_grpc.pb.rs); } } }注意方式三中proto与grpc分属不同模块此时应结合message_module_path(...)让服务桩代码能定位到消息模块且消息代码generated.rs与服务代码{stem}_grpc.pb.rs需要分别include!因为二者已不再共处同一个模块。八、仓库内的真实应用范例本仓库自身多处使用本库可作为实战模板1. examples 项目helloworld / routeguide / GCP 客户端——examples/build.rs 中以GRPC_RUST_REGENERATE_PROTO环境变量控制是否重新生成默认直接使用检入仓库的examples/generated代码从而无需安装 protoc 即可编译生成时分别用client_only()只生成 helloworld 客户端该示例的服务器端由 tonic 提供对 routeguide 走全量生成GCP 相关 protogoogle/pubsub/v1/pubsub.proto、schema.proto及google/api系列组合使用inputs([...])、dependencies(...)与client_only()。2. 基准测试grpc-benchmark——grpc-benchmark/build.rs 一次性编译 7 个 benchmark 相关 protobenchmark_service.proto、messages.proto、worker_service.proto、control.proto、payloads.proto、stats.proto、grpc/core/stats.proto并注入protobuf_well_known_types依赖。3. 互操作测试interop——interop/build.rs 用include(proto/grpc/testing)后编译test.proto、empty.proto、messages.proto三个文件与tonic_prost_build的产物在同一构建中共存。4. grpc-protobuf crate——grpc-protobuf/build.rs 生成google/rpc/status.proto输入目录指向third_party/googleapis同样配合 well-known types 依赖并启用client_only()。这些用例共同验证了一个模式客户端为主的 crate 普遍开启client_only()缩减生成面涉及 Google 标准 proto 时注入protobuf_well_known_types依赖CI/无工具链环境下通过环境变量与检入的生成代码保证可复现构建。九、常见问题与注意事项找不到 protoc / 插件按 grpc-protobuf-build/src/lib.rs 的报错提示四选一启用build-pluginfeature 源码编译、设置GRPC_RUST_PROTOC_DIR、加入 PATH、或在 build.rs 中prebuilt_binaries(...)显式指定。消息代码重复生成若消息代码由其他工具如protobuf_codegen单独生成务必generate_message_code(false)否则protobuf_codegen与protoc-gen-rust-grpc会对同一 proto 产出冲突的消息类型。模块路径不匹配导致编译失败消息与服务代码分属不同模块时message_module_path必须指向消息代码所在的真实 Rust 路径如crate::pb::messages默认值self仅适用于同模块场景。文件缺失是正常的无 service 的 proto 不会产生_grpc.pb.rs无消息的 proto 不会产生generated.rsformat_code()已对文件不存在做了容错。API 不稳定当前为 preview 版本升级前务必阅读对应版本的 CHANGELOG仓库根目录 CHANGELOG.md确认 API 变更。掌握以上配置与流程后无论你的服务代码面向grpccrate 还是toniccrate都可以用同一份build.rs配置稳定地产出消息与服务桩代码并将生成物灵活落地到OUT_DIR或仓库内的自选目录。赞分享后端RPC框架【免费下载链接】grpc-rustA native gRPC client server implementation with async/await support.项目地址https://gitcode.com/GitHub_Trending/to/grpc-rust点击查看免费下载相关推荐RisingWave 中编写 gRPC/Tonic 服务完整指南从 proto 定义到服务注册与启动RisingWave 中编写 gRPC/Tonic 服务完整指南从 proto 定义到服务注册与启动 RisingWave 的各个组件meta、comput数据库流处理后端数据工程使用 Ent 与 gRPC 集成从 Schema 自动生成 Protobuf 与 gRPC 服务使用 Ent 与 gRPC 集成从 Schema 自动生成 Protobuf 与 gRPC 服务 导读 本文讲解 EntGo 实体框架与 gRPC 的官方后端ORM代码生成gh_mirrors/proto/protobuf在gRPC中的集成使用构建高性能RPC服务的完整流程gh_mirrors/proto/protobuf在gRPC中的集成使用构建高性能RPC服务的完整流程 Protocol Buffers简称protobuf序列化上一篇一张截图跑通 UI 测试Midscene.js AI 视觉自动化实战指南下一篇gh_mirrors/re/redis-3.0-annotated源码解析Redis中的多线程模型与并发控制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考