protobuf
protoc 版本
协议文件 版本
message 消息中承载的数据分别对应于每一个字段都有一个名字和一种类型
optional
repeated :在格式正确的消息中,此字段类型可以重复零次或多次。系统会保留重复值的顺序
字段规则 字段类型 字段名称=字段编号[default=0];
字段类型:
基本数据类型:bool、int32、int64、uint32、uint64、float、double、string、bytes。
标量类型(int、string等),也可以是复合类型(enum等),也可以是其他message
字段编码
Protobuf编码 是通过成员的唯一编号来绑定对应的数据
message成员编号,可以不从1开始,但是不能重复,不能使用19000 - 19999
标识号是[0,2^29-1]范围内的一个整数
解析数据时:
解析数据时 不同类型的默认值不同
对于枚举,默认值是第一个定义的枚举值,该值必须为0
repeated字段默认值是空列表
// enum为关键字,作用为定义一种枚举类型
enum 定义消息类型时,可能会希望其中一个字段有一个预定义的值列表
可以通过enum在消息定义中添加每个可能值的常量来非常简单的执行此操作
enum的第一个常量映射为0,
每个枚举定义必须包含一个映射到零的常量作为其第一个元素
不同的枚举常量指定相同的值来定义别名。如果想要使用这个功能必须将allow_alias选项设置为true,负责编译器将报错
oneof关键字
如果有一个包含许多字段的消息,并且最多只能同时设置其中的一个字段,则可以使用oneof功能
ProtoBuf编码和解析
大量已经定义好的proto文件,其实这些文件是Protobuf的描述文件,类似元数据。
用本身的语法描述本身,同时通过这些文件生成对应的语言的元数据类等代码
01.描述文件中最重要的文件 就是descriptor.proto 这个文件,
这个文件是整个proto语法的描述类,描述了实际Protobuf各层次语法的结构
02.其他proto文件
解析器:
编码时
解析时:
ParseFromString(serialized_data)
SerializeToString()
###机制
DescriptorPool类根据 type name 拿到一个 Descriptor的对象指针,
在通过MessageFactory工厂类根据Descriptor实例构造出具体的Message对象。
一个当前 Message 对象的 Descriptor 实例,
这个 Descriptor 实例主要保存 Message 的源文件 Descriptor 和每个 field 的 Descriptor,
然后通过循环的方式对 Message 的每个 field 进行赋值
gRPC(gRPC Remote Procedure Calls)
gRPC默认使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制
服务发现-注册中心 服务和服务之间调用需要使用RPC
protoc生成代码时加上参数–descriptor_set_out,输出类型信息到一个文件 (即SelfDescribingMessage的第一个字段内容)
ProtoBuf
01.静态消息 与 动态消息
动态消息的使用方式
有两种方式:
一种是运行时设置需要的字段名、字段类型等;
另外一种是运行前定义一个proto文件设置需要的字段名、字段类型等,线上动态编译这个proto文件,
并调用相关的消息描述子(Descriptor)、字段描述子(FieldDescriptor)、反射器(Reflection)去读、写相应的字段
02.Protobuf 动态加载 .proto 文件并操作 Message
03.Protobuf反射 )google::protobuf::Reflection 反射对象, 通过它 + FieldDescriptor, 能set/get filed对象的值
反射的核心要点是:获取程序元信息。
反射机制的关键类为Descriptor类
解析proto文件时,肯定需要先将其解析为抽象语法树(AST)
FileDescriptorProto 就是我们要找的AST结构
/* 反射创建实例 */
auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("Dog");
auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
auto instance = prototype->New();
/* 反射相关接口 */
auto reflecter = instance.GetReflection();
auto field = descriptor->FindFieldByName("name");
reflecter->SetString(&instance, field, "美") ;
// 获取属性的值.
std::cout<<reflecter->GetString(instance , field)<< std::endl ;
return 0 ;
04.非 .proto 文件
从远程读取,如将数据与数据元信息一同进行 protobuf 编码并传输
05. 利用了
DescriptorPool 从 FileDescriptorProto 解析出 FileDescriptor(描述 .proto 文件中所有的 messages)。
然后用 DynamicMessageFactory 从 FileDescriptor 里找到我们关注的那个 message 的MessageDescriptor。
接下来,我们利用 DynamicMessageFactory 根据 MessageDescriptor 得到一个prototype message instance。
proto_desc.proto
apollo/cyber/message/protobuf_factory.cc
RegisterMessage()
apollo/cyber/message.protobuf_factory.h
FileDescriptor (google::protobuf::FileDescriptor) FileDescriptorProto Descriptor DescriptorPool DynamicMessageFactory
ProtoDesc (appllo::cyber::proto::ProtoDesc)
python
from google.protobuf import message_factory ,descriptor_pb2, descriptor_pool
CyberRT
核心类是 Component 和 TimerComponent;
支撑 component 的是 Node、Scheduler、Timer、DataVisitor;
module(模块)和component(组件),在Cyber RT中,一个module可以由多个component组成。
代码解析
自身的消息描述descriptor和它依赖的所有消息的descriptor,都放入 descriptor_pool,之后就可以根据消息类型来创建消息了。
1.record.proto
SingIndex oneof cache{ ChannekCache ChunkBodyCache ChunkHeaderCache}
proto_desc.proto
Channel中的proto_desc反序列化为 Chatter 对象
2.proto_desc_pb2
record_pb2
record_pb2.ChunkHeader() .begin_time .end_time message_number raw_size
record_pb2.ChunkBody() SingleMessage
SingleMessage
message.channel_name
message.time
message.content SerializeToString()
3.chunk <---- cyber.proto
proto_chunk_header
proto_chunk_body
4.common <--- Enum
class Compression
Class Section
Header_length Section_length
chunk_interval chunk_raw_size segment_interval segment_raw_size
5. ---> Reader
google.protobuf
common
cyber.proto
file_object.chunk
record_exception ---> Reader
6.---> Write
google.protobuf
common
cyber.proto
file_object.chunk ---> Write
7. ---> Record
google.protobuf
common
cyber.proto
writer
reader ---> Record
8.---> main
cyber.proto
Record ---> main
反射
01.构造DescriptorPool
02.获取Descriptor: const Descriptor *descriptor = importer.pool()->FindMessageTypeByName("Pair");
03.通过Descriptor 获取Message: const Message *message = factory.GetPrototype(descriptor);
参考
cyber record包解析工具 https://zhuanlan.zhihu.com/p/499516617
https://github.com/daohu527/cyber_record/tree/main
Protobuf动态解析那些事儿 https://www.cnblogs.com/jacksu-tencent/p/3447310.html
标签:反射,Protobuf,descriptor,proto,Descriptor,文件,message,动态
From: https://www.cnblogs.com/ytwang/p/18131873