前言
tonic是rust语言中最被广泛使用的框架,但是基于tonic的grpc信息获取,会丢失option信息。
出现的情况
- 当我们在grpc的service中加入一下option信息时,如下:
service HelloService{
rpc HelloWorld(HelloWorldRequest)returns(HelloWorldResponse){
option (google.api.http) = {
post: "/api/v1/hello"
body: "*"
};
}
- 在
build.rs中,从proto文件到rs文件的代码构建中,生成指定描述文件
tonic_build::configure()
...
.file_descriptor_set_path("./src/proto/services_descriptor.bin")
...
- 使用tonic提供的tonic-reflection包提供反射服务。
pub(crate) const FILE_DESCRIPTOR_SET: &[u8] = include_bytes!("../proto/services_descriptor.bin");
...
let reflect_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
.build()
.unwrap();
...
tonic::transport::Server::builder()
...
.add_service(reflect_service)
...
- 调用这个反射服务,打印service描述,会发现丢失了option的内容。
排查过程
遇到这个问题,首先怀疑描述文件中遗漏了option。
虽然生成的描述文件不可读,但我们以text方式打开,进行人肉读取,还是能找到这部分内容的。截图如下。
发现信息没有丢失后,检查tonic-reflection包的数据处理方式。
tonic的protobuf能力是prost包提供的,那类型包就应该是prost_types,检查一下依赖,果然如此。
看一下它的method结构,果然少了google.protobuf.MethodOptions.special_fields字段
结论
因为prost包的方法描述结构 缺失了special_fields字段导致的。
修复方法
将tonic-reflection包替换为tonic-reflect-protobuf,同时将上面build反射服务的代码替换如下:
// let reflect_service = tonic_reflection::server::Builder::configure()
let reflect_service = tonic_reflect_protobuf::server::Builder::configure()
...
md吐槽
这个问题在2021年的时候就遇到了,当时tonic功能还不是很完善,我在discord的tonic频道中提到这个问题。截图如下:
tonic作者LucioFranco表示,这锅我可不背,你找prost去吧。
试图联系prost解决未果,期待后续升级能解决该问题。
two year later...
prost不断升级,但该问题一直存在,终于忍无可忍,无需再忍,提个pr吧。
作者表示,要搞你自己去搞,反正我不支持。
既然如此,就先搞个三方包将就着吧tonic-reflect-protobuf
未完待续。。。