Protobuf_动态消息-反射
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
Python
解析 pb 的数据
def load_protobuf_from_file(container, filename)
load_form_text_file() from google.protobuf import text_format text_format.Parse
load_from_binary_file() obj.ParseFromString()
google.protobuf.text_format.PrintMessage()
google.protobuf.text_format.Merge
参考: https://github.com/microsoft/MMdnn/blob/master/mmdnn/conversion/common/IR/IR_graph.py
def load_protobuf_from_file(container, filename):
with open(filename, 'rb') as fin:
file_content = fin.read()
# First try to read it as a binary file.
try:
container.ParseFromString(file_content)
return container
except Exception as e: # pylint: disable=broad-except
print ("Info: Trying to parse file [%s] with binary format but failed with error [%s]." % (filename, str(e)))
# Next try to read it as a text file.
try:
from google.protobuf import text_format
text_format.Parse(file_content.decode('UTF-8'), container, allow_unknown_extension=True)
except text_format.ParseError as e:
raise IOError("Cannot parse file %s: %s." % (filename, str(e)))
return container
###使用Python脚本自动编译proto文件
protoc --python_out=. example.proto
方式一:
根据proto文件编译生成的类来反序列化消息
方式二:
ProtoBuf提供了动态解析机制来
通过proto文件生成的descriptor来构造动态消息类,然后反序列化(解析)消息
https://blog.51cto.com/u_16175463/9923630
C++
proto::object.SerializeToString(&rspBuf) proto::object.ParseFromString(buf) 一对关系
proto::object.Utf8DebugString() google::protobuf::TextFormat::ParseFromString
proto::object.SerializeToOstream() proto::object.ParseFromIstream()
google::protobuf::TextFormat::Print() google::protobuf::TextFormat::Parser()
PrintToString ParseFromString
ParsePartialFromFileDescriptor
ParsePartialFromIstream
参考: Protobuf读写Prototxt https://zhuanlan.zhihu.com/p/423086300
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
分类:
ROS
, 终端_C++_C 语言
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2022-04-12 Python_数据处理项目开发总结-代码设计