如何从.proto文件获取消息对应的prost_types::DescriptorProto?
我用Rust编写了一个程序,通过gcloud-sdk的google.cloud.bigquery.storage.v1,借助gRPC和Protobuf调用Google BigQuery Storage写入API。
报错信息
2023-02-17T08:16:04.492220Z DEBUG gcloud_sdk::middleware: OK: /google.cloud.bigquery.storage.v1.BigQueryWrite/AppendRows took 262ms (incl. token gen: 0ms) Error: Status { code: InvalidArgument, message: "Invalid proto schema: BqMessage.proto: my.proto: "my.proto" is not a valid identifier. Entity: projects/steam-sequencer-xxxxxx/datasets/my_dataset/tables/test_table/streams/_default", source: None }
核心困惑
我对ProtoSchema结构体的参数存在疑问,它的定义如下:
pub struct ProtoSchema { pub proto_descriptor: Option<DescriptorProto>, }
ProtoSchema用于描述序列化Protocol Buffer数据行的schema,其中proto_descriptor字段类型为prost_types::DescriptorProto。很多语言里这个类型可通过语言或包原生生成,但我作为Rust、Prost和gRPC新手,不知道如何通过Prost/Tonic crate从.proto文件获取DescriptorProto结构体。
我编写的相关代码
let x = DescriptorProto { name: Some(String::from("my.proto")), field: vec![], extension: vec![], nested_type: vec![], enum_type: vec![], extension_range: vec![], oneof_decl: vec![], options: None, reserved_range: vec![], reserved_name: vec![], }; let proto_schema = ProtoSchema { proto_descriptor: Some(x), }; let proto_data = ProtoData { writer_schema: Some(proto_schema), rows: Some(proto_rows), };
解决方案
1. 先解决报错根源
你手动构造的DescriptorProto把name设为了"my.proto",但这个字段需要的是proto消息类型的名称(比如MyMessage),而非.proto文件名,这就是报错"my.proto" is not a valid identifier的直接原因。
2. 从.proto文件生成DescriptorProto的正确步骤
步骤1:配置Cargo.toml
确保依赖包含prost、prost-types、prost-build及tonic:
[dependencies] prost = "0.12" prost-types = "0.12" tonic = "0.10" [build-dependencies] prost-build = "0.12"
步骤2:编写build.rs编译proto文件
在项目根目录创建build.rs,启用描述符生成选项:
fn main() -> Result<(), Box<dyn std::error::Error>> { prost_build::Config::new() // 指定生成的描述符集文件路径 .file_descriptor_set_path("src/proto_descriptor.bin") // 编译你的proto文件,替换为实际路径 .compile_protos(&["src/my.proto"], &["src/"])?; Ok(()) }
步骤3:加载描述符集并提取DescriptorProto
编译后会生成proto_descriptor.bin,运行时加载并解析出目标DescriptorProto:
use prost_types::{FileDescriptorSet, DescriptorProto}; use std::fs::read; fn load_target_descriptor() -> Option<DescriptorProto> { // 读取描述符集文件 let descriptor_bytes = read("src/proto_descriptor.bin").ok()?; let file_set = FileDescriptorSet::decode(&descriptor_bytes[..]).ok()?; // 找到目标消息的DescriptorProto,替换为你的消息类型名 file_set.file.into_iter() .find(|file| file.name.as_deref() == Some("my.proto"))? .message_type.into_iter() .find(|msg| msg.name.as_deref() == Some("MyMessage")) } // 在业务代码中使用 let descriptor_proto = load_target_descriptor().expect("加载DescriptorProto失败"); let proto_schema = ProtoSchema { proto_descriptor: Some(descriptor_proto), };
步骤4:确保proto文件定义规范
你的my.proto需要包含合法的消息定义,示例:
syntax = "proto3"; message MyMessage { string id = 1; int64 value = 2; // 其他业务字段... }
3. 额外注意事项
- 确保BigQuery表的schema与proto消息字段完全匹配,包括字段名、数据类型
- 验证
proto_rows的序列化格式与proto_schema定义一致,避免数据格式不匹配问题
内容的提问来源于stack exchange,提问作者WENPIN1

