记一次排查,tonic反射信息不全的问题

500 阅读2分钟

前言

tonic是rust语言中最被广泛使用的框架,但是基于tonic的grpc信息获取,会丢失option信息。

出现的情况

  1. 当我们在grpc的service中加入一下option信息时,如下:
service HelloService{
  rpc HelloWorld(HelloWorldRequest)returns(HelloWorldResponse){
    option (google.api.http) = {
      post: "/api/v1/hello"
      body: "*"
    };
  }
  1. build.rs中,从proto文件到rs文件的代码构建中,生成指定描述文件
tonic_build::configure()
...
    .file_descriptor_set_path("./src/proto/services_descriptor.bin")
...
  1. 使用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)
...
  1. 调用这个反射服务,打印service描述,会发现丢失了option的内容。

排查过程

遇到这个问题,首先怀疑描述文件中遗漏了option。

虽然生成的描述文件不可读,但我们以text方式打开,进行人肉读取,还是能找到这部分内容的。截图如下。

image.png

发现信息没有丢失后,检查tonic-reflection包的数据处理方式。

tonic的protobuf能力是prost包提供的,那类型包就应该是prost_types,检查一下依赖,果然如此。

image.png

看一下它的method结构,果然少了google.protobuf.MethodOptions.special_fields字段

image.png

结论

因为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频道中提到这个问题。截图如下:

image.png

tonic作者LucioFranco表示,这锅我可不背,你找prost去吧。

image.png

试图联系prost解决未果,期待后续升级能解决该问题。

two year later...

prost不断升级,但该问题一直存在,终于忍无可忍,无需再忍,提个pr吧。

image.png

作者表示,要搞你自己去搞,反正我不支持。

image.png

既然如此,就先搞个三方包将就着吧tonic-reflect-protobuf

未完待续。。。