Protobuf 扩展指南

本文并非 Protobuf 的基础、语法介绍,更加关注 Protobuf 的扩展用法以及实际案例基础

这部分可以参考官方文档,proto3的语法在这里只做简要的介绍和整理。

一个 基础Message 的定义如下

syntax = "proto3";
import  "other.proto";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  1. 字段一般是以 [ "repeated" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";" 格式定义的
  2. 通过 protoc 即其插件,这个 proto 类型的文件会被生成特定语言的结构体,这种语言里面的类型和 proto 文件中的基础类型对应关系在这里,在其他类型的对应关系上,比如 enum、timestamp、duration ( timestamp, duration 为拓展类型)等,由于不同语言的实现方式不同,转换方式也有所不同,取决于 protoc 或插件的实现。
  3. protobuf (如无特别说明,下文中指 protobuf 3)的数据结构通过一个 repeated 关键字实现,同时 v3 也支持了 map 类型。
  4. protobuf 支持嵌套,不支持继承。支持 any,oneof 等特殊的结构,实际取值方式和特定语言有关。
  5. protobuf 的结构体支持转换为 json 而非二进制格式,对应关系在这里,这点值得注意,一般来说一般语言中的结构体转换 json 有自己的转换库函数,但是如果使用 protobuf 的库来转换可能转换结构有所不同,本质原因是因为 protobuf 的库转换时的标准不同。
  6. option的定义格式是 "option" optionName "=" constant ";", 比如 option java_package = "com.example.foo"; options 有内置也有自定义的。这部分和高级部分关系比较大。
    1. options并不改变整个文件声明的含义,但却能够影响特定环境下处理方式。完整的内置选项可以在 google/protobuf/descriptor.proto 找到,不同的 option 和他所在的位置对应。
    2. option 有多种类型,比如 fileOption, fieldOption, methodOption 等等
    3. 当你需要自定义一些 option,方式是使用 proto2 的 extend 语法,下面给出了一个例子
import "google/protobuf/descriptor.proto";
extend google.protobuf.MessageOptions {
  optional string my_option = 51234;
}
message MyMessage {
  option (my_option) = "Hello world!";
}

// Java 中获取这个 option 
value = my_proto_file_pb2.MyMessage.DESCRIPTOR.GetOptions()
  .Extensions[my_proto_file_pb2.my_option]

另一个真实的例子,来自 google 的 http 扩展,这里插件会获取 名为 google.api.http 的 option,然后转换为 http 结构

extend google.protobuf.MethodOptions {
  // See `HttpRule`.
  HttpRule http = 72295728;
}


// 实际使用
service Messaging {
    rpc GetMessage(GetMessageRequest) returns (Message) {
        option (google.api.http) = {
                get:"/v1/messages/{message_id}"
            };
    }
}

扩展

protobuf 描述一切

本质上,protobuf 的能力是描述 entitymethod,基本上 entity、method 外加一些约定,就可以描述所有的协议了。事实上 google 的 api 定义 基本上都可以都可以找到 protobuf 的描述

这里 protobuf 的描述作用就可以是

  1. 一个可以被复用的类型 (或者是一种 WellKnownType)。
  2. 一个 rpc 服务的输入输出类型,或者 rpc 服务的 service 以及 method。
  3. 一个描述某种协议(基于 protobuf 扩展 )的元信息结构和扩展位置的约定。
  4. 其他各种内置或扩展的 proto 文件元信息 key value。

甚至,protobuf 能够描述 protobuf 自己。protoc 以及插件的解析 proto 文件原理中最重要的是一个 descriptor 结构,而这个结构也是 protobuf 描述的,这是一个鸡生蛋还是蛋生鸡的关系,事实上,最初的 descriptor 是开发 compile 的开发者手动写的,经过一段时间,再用 protoc 生成 descriptor 文件,用于 protoc 文件 (似乎是一种循环依赖)。

http 扩展

如上所述,google api 中定义了如何将 grpc 映射成 http 的协议,理解这套协议以及实现,是理解扩展 protobuf 的一个很好的出发点。

  1. 首先定义 映射协议以及描述对应关系的 entity, 这个 entity比较简单,文件 大部分实在描述映射的协议,以便于实际实现方参考这个描述来实现。
  2. 使用 protobuf 的 extend option 的方法来扩展协议,这之后就可以使用相关的关键字来定义 google.http.rule, 比如 option (google.api.http) = {get:"/v1/messages/{message_id}}
  3. 实现插件,使用相关的 descriptor 提取 proto 中的信息,转换为 httpRule 结构体,比如 grpc-ecosystem/grpc-gateway 里面的 protoc-gen-grpc-gateway 插件就利用这种方法提取出了 httpRule 结构,然后利用这种结构来实现来 grpc 方法对应的 http handler。比如这个函数 就是提取 httpRule 结构的方法。至此就实现了 结构、协议、proto 文件、生成文件直接的对应转换。
protobuf-http-extension

gogo 扩展

gogo-protobuf 是 protoc 的 go 语言插件的实现,在实现特定语言代码生成的基础了,实现了多种 扩展特性,原始的定义在 这里 , 有以下几类

  1. google.protobuf.EnumOptions/EnumValueOptions:Enum 选项,如 goproto_enum_prefix 表示 enum 前缀开关
  2. google.protobuf.FileOptions:全局/文件选项,如goproto_getters_all 是全局的打开是否生成 get 函数的开关,也有对应的 MessageOptions - goproto_gette
  3. google.protobuf.MessageOptions:类型选项,同上,只是作用范围不同
  4. oogle.protobuf.FieldOptions:字段选项,比较常用的有 nullable - 表示生成指针还是结构体,stdtime 表示转换 WellKnownType timestamp 为 time.Time 等

实际实现和一般的 protoc 的插件并无不同,descriptor 结构由 protoc 解析,插件从 descriptor 进一步的解析出 proto 文件结构,以及各种扩展的选项,然后生成go 语言的文件。

以 nullable 这个选项为例,生成语言文件的时候会使用 帮助函数 判断对应 field 是否设置了 Nullable 的 Extension,如果没设置或者设置为True,则生成的结构则带指针,默认值为 nil。

参考

  1. https://colobu.com/2019/10/03/protobuf-ultimate-tutorial-in-go/
  2. https://colobu.com/2015/01/07/Protobuf-language-guide/
  3. https://blog.csdn.net/xeseo/article/details/12832577
本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
Protobuf 扩展指南
另一个真实的例子,来自 google 的 http 扩展,这里插件会获取 名为 google.api.http 的 option,然后转换为 http 结构
<<上一篇
下一篇>>