首页 > 其他分享 >Protobuf_动态消息-反射

Protobuf_动态消息-反射

时间:2024-04-12 18:24:12浏览次数:22  
标签:反射 Protobuf descriptor proto Descriptor 文件 message 动态

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

相关文章

  • 动态规划
    1、动态规划概述动态规划是一种解决多阶段决策问题的数学优化方法。它将原问题分解成若干个子问题,通过解决子问题只需解决一次并将结果保存下来,从而避免了重复计算,提高了算法效率。通俗来讲,动态规划算法是解决一类具有重叠子问题和最优子结构性质的问题的有效方法。其基本原理......
  • MyBatis动态SQL
    MyBatis动态SQL动态SQL简介动态SQL是MyBatis的强大特性之一。如果你使用过JDBC或其它类似的框架,你应该能理解根据不同条件拼接SQL语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态SQL,可以彻底摆脱这种痛苦。使用动态......
  • C++——线性动态规划
    线性动态规划引入:1.爬楼梯爬楼梯类型的问题可谓是线性DP的入门题目以及经典中的经典。我们先来看一下题目。爬楼梯题目描述有一天,三萩实在太无聊了,竟然无聊到去数台阶了。有一个楼梯一共有m级,刚开始三萩在第一级,他就想,若每次只能跨上一级或者二级,要走上m级,共有多少种走法?......
  • Protobuf - Designing Effective APIs
       ......
  • el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能,利用@sort-cha
        写这篇博客的原因是前段时间做了一个数据列可变的表格,同时需要实现在网页中更新了数据列之后,能够对表格进行排序的需求。如果想要直接了解实现el-table的动态数据动态排序(列数据是通过计算获得,并且可以在页面中修改,在此基础上实现数据变化后实时更新)。请直接跳到......
  • 多级动态表头导出-easyexcel
    导出如下动态表头 主要的构造tabCols和tableData,注意表头的字段,基本构造出了该格式所有的都能适配@GetMapping("/exportData")publicvoidexcelExport(TbDtTargetHealthMontbDtTargetHealthMon,HttpServletResponseresponse)throwsIOException{re......
  • Day37代码随想录(1刷) 动态规划
    509.斐波那契数斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:F(0)=0,F(1) =1F(n)=F(n-1)+F(n-2),其中n>1给定 n ,请计算 F(n) 。示例1:输入:n=2输出:1解......
  • 深入浅出Go语言:反射应用解密!
    深入浅出Go语言:反射应用解密!原创 麻凡 麻凡 2024-03-0609:02 湖南 1人听过在Go语言编程中,反射是一项高级技术,能够在程序运行时检查变量和值的类型。你可能会问,为什么我们需要反射呢?让我们一起揭开这个神秘的面纱。为什么需要反射大多数情况下,我们在编写代码时都可......
  • 动态规划dp
    动态规划(Dp)介绍动态规划(Dynamicprogramming)是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。动态规划问题的特点1.最优子结构性质:如果问题的最优......
  • Ubuntu-kali配置动态ip(简单)
    使用gedit文本编辑器打开网络接口配置文件gedit/etc/network/interfaces新增两行内容如下:autoeth0ifaceeth0inetdhcp其意思为:网卡开机自动挂载,以DHCP方式配置网卡。点击保存后叉掉。同样,使用文本编辑器打开resolv.conf配置文件。该配置文件主要作用是解析域名ged......