2019独角兽企业重金招聘Python工程师标准>>>

需求背景

在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做反序列化。“自动”的意思主要有两个方面:(1)当程序中新增一个 protobuf Message 类型时,这部分代码不需要修改,不需要自己去注册消息类型,不需要重启进程,只需要提供protobuf文件;(2)当protobuf Message修改后,这部分代码不需要修改,不需要自己去注册消息类型,不需要重启进程只需要提供修改后protobuf文件。

技术介绍

Protobuf的入门可以参考 Google Protocol Buffer 的在线帮助 网页  或者IBM developerwor上的文章 《Google Protocol Buffer 的使用和原理》 。

protobuf的动态解析在google protobuf buffer官网并没有什么介绍。通过google出的一些参考文档可以知道,其实,Google Protobuf 本身具有很强的反射(reflection)功能,可以根据 type name 创建具体类型的 Message 对象,我们直接利用即可,应该就可以满足上面的需求。

实现可以参考淘宝的文章 《玩转Protocol Buffers 》 ,里面对protobuf的动态解析的原理做了详细的介绍,在此我介绍一下Protobuf  class diagram。

大家通常关心和使用的是图的左半部分:MessageLite、Message、Generated Message Types (Person, AddressBook) 等,而较少注意到图的右半部分:Descriptor, DescriptorPool, MessageFactory。

上图中,其关键作用的是 Descriptor class,每个具体 Message Type 对应一个 Descriptor 对象。尽管我们没有直接调用它的函数,但是Descriptor在“根据 type name 创建具体类型的 Message 对象”中扮演了重要的角色,起了桥梁作用。上图的红色箭头描述了根据 type name 创建具体 Message 对象的过程。

实现

先直接上代码,这个代码来自于 《玩转Protocol Buffers 》 :

#include <google/protobuf/descriptor.h>

#include <google/protobuf/descriptor.pb.h>

#include <google/protobuf/dynamic_message.h>

#include <google/protobuf/compiler/importer.h>

using namespace google::protobuf;

using namespace google::protobuf::compiler;

int main(int argc,const char *argv[])

DiskSourceTree sourceTree;

//look up .proto file in current directory

sourceTree.MapPath("","./");

Importer importer(&sourceTree, NULL);

//runtime compile foo.proto

importer.Import("foo.proto");

const Descriptor *descriptor =    importer.pool()->

FindMessageTypeByName("Pair");

cout << descriptor->DebugString();

// build a dynamic message by "Pair" proto

DynamicMessageFactory factory;

const Message *message = factory.GetPrototype(descriptor);

// create a real instance of "Pair"

Message *pair = message->New();

// write the "Pair" instance by reflection

const Reflection *reflection = pair->GetReflection();

const FieldDescriptor *field = NULL;

field = descriptor->FindFieldByName("key");

reflection->SetString(pair, field,"my key");

field = descriptor->FindFieldByName("value");

reflection->SetUInt32(pair, field, 1111);

cout << pair->DebugString();

那我们就来看看上面的代码

1 )把本地地址映射为虚拟地址

DiskSourceTree sourceTree;

//look up .proto file in current directory

sourceTree.MapPath("","./");

2 )构造 DescriptorPool

Importer importer(&sourceTree, NULL);

//runtime compile foo.proto

importer.Import("foo.proto");

3 )获取 Descriptor

const Descriptor *descriptor = importer.pool()->FindMessageTypeByName("Pair");

4 )通过 Descriptor获取Message

const Message *message = factory.GetPrototype(descriptor);

5 ) 根据类型信息使用 DynamicMessage new 出这个类型的一个空对象

Message *pair = message->New();

6 ) 通过 Message 的 reflection 操作 message 的各个字段

const Reflection *reflection = pair->GetReflection();

const FieldDescriptor *field = NULL;

field = descriptor->FindFieldByName("key");

reflection->SetString(pair, field,"my key");

field = descriptor->FindFieldByName("value");

reflection->SetUInt32(pair, field, 1111);

直接copy上面代码看起来我们上面的需求就满足了,只是唯一的缺点就是每次来个包加载一次配置文件,当时觉得性能应该和读取磁盘的性能差不多,但是经过测试性能极差,一个进程每秒尽可以处理1000多个包,经过分析性能瓶颈不在磁盘,而在频繁调用malloc和free上。

看来我们得重新考虑实现,初步的实现想法:只有protobuf描述文件更新时再重新加载,没有更新来包只需要使用加载好的解析就可以。这个方案看起来挺好的,性能应该不错,经过测试,性能确实可以,每秒可以处理3万左右的包,但是实现中遇到了困难。要更新原来的Message,必须更新Importer和Factory,那么要更新这些东西,就涉及到了资源的释放。经过研究这些资源的释放顺序特别重要,下面就介绍一下protobuf相关资源释放策略。

动态的Message是我们用DynamicMessageFactory构造出来的,因此销毁Message必须用同一个DynamicMessageFactory。 动态更新.proto文件时,我们销毁老的并使用新的DynamicMessageFactory,在销毁DynamicMessageFactory之前,必须先删除所有经过它构造的Message。

原理:DynamicMessageFactory里面包含DynamicMessage的共享信息,析构DynamicMessage时需要用到。生存期必须保持Descriptor>DynamicMessageFactory>DynamicMessage。

释放顺序必须是:释放所有DynamicMessage,释放DynamicMessageFactory,释放Importer。

总结

资源释放前,必须要了解资源的构造原理,通过构造原理反推释放顺序,这样就少走弯路、甚至不走。

参考文献

Google Protocol Buffer 的在线帮助 网页

一种自动反射消息类型的 Google Protobuf 网络传输方案

《玩转Protocol Buffers 》

《Google Protocol Buffer 的使用和原理》

google protocol buffer 动态创建描述和动态解析

protocol buffer动态解析

标签: protocol buffers

转载于:https://my.oschina.net/u/1426828/blog/833299

Protobuf动态解析那些事儿相关推荐

  1. Protobuf动态解析

    阅读目录 需求背景 技术介绍 实现 总结 参考文献 回到顶部 需求背景 在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做反序列化."自动 ...

  2. fetchtype 动态控制_RouterOS利用aliyun的API接口实现DDNS动态解析

    本文主要讲解如何借助阿里云aliyun的云解析API接口来实现RouterOS(以下简称ROS)的DDNS动态解析. 一.创建访问控制RAM的AccessKey 我这边简单的讲讲如何申请开通: 1.阿 ...

  3. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)...

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  4. Java动态解析域名

    Java动态解析域名 Java提供InetAddress类,可以对域名-IP进行正向.逆向解析. InetAddress解析的时候一般是调用系统自带的DNS程序. linux 默认的DNS方式是读取/ ...

  5. 基于`IRIS`,动态解析`HL7`消息

    文章目录 基于`IRIS`,动态解析`HL7`消息 什么是`HL7` `HL7` 版本 `HL7` 消息结构 段(`Segment`) 字段(`Field`) `HL7` 数据类型 在`IRIS`中查 ...

  6. Protobuf数据格式解析

    Protobuf数据格式解析 Protobuf是Google开源的一款类似于Json,XML数据交换格式,其内部数据是纯二进制格式,不依赖于语言和平台,具有简单,数据量小,快速等优点.目前用于序列化与 ...

  7. 家庭公网IP动态解析至阿里云DNS

    家庭公网IP动态解析之阿里云DNS 此服务使用Java开发,每隔10分钟进行阿里云dns解析.如果解析地址未变更,则不出发修改解析操作. 代码 1. AliClient 代码 获取指定域名的解析记录和 ...

  8. python3实现阿里云DDNS域名动态解析

    一. 前言 家里部署了一台NAS服务器,在公司平时都是通过IP访问的,现在想更改为用域名去访问,但是家里的宽带都是动态的公网IP,每次IP变了都需要手动解析一次域名,这样就比较麻烦,那怎么办了?这个时 ...

  9. 利用NAS免费部署动态解析实现内网穿透

    ‍ 想要从外网访问家中的NAS等设备,一般来说我们需要知道家中路由器的公网IP. 现在固定的公网IP基本上很难免费申请到了,但是一般来说运营商可以免费提供一个动态变化的公网IP:当路由设备重启时,运营 ...

最新文章

  1. SpringBoot学习:整合shiro(身份认证和权限认证),使用EhCache缓存
  2. Ember版本小小结
  3. FreeWheel是一家怎样的公司?| 人物志
  4. hbuilder - wap to app
  5. JS兼容问题的函数封装文档
  6. c语言 callback回调函数
  7. 2018年秋计算机应用基础本科,广东开放大学远程教育专科2018年秋计算机应用基础Word模块测试...
  8. python机器学习库keras——AutoEncoder自编码、特征压缩
  9. d soft php package,让程序飞起来之 Laravel OPcache Package
  10. Exchange Server 2016体验
  11. 迈高图手机版_迈高图地图数据下载器
  12. css设置div垂直居中
  13. 如何将图片批量转换成PDF
  14. 显卡天梯图2021年9月新版
  15. 解决NIVIDIA控制面板开启不了的问题
  16. linux sox录音时间控制,Linux 对音频万能处理的命令——SOX
  17. day 82 Vue学习三之vue组件
  18. 到底什么是非线性优化?
  19. icraft服务器网页图片,iCraft家族添新一代顶级显卡
  20. win10锁屏后默认1分钟进入睡眠状态的解决办法

热门文章

  1. python导入data-Python通过load data导入MySQL数据
  2. mysql 性能分析_十大MySQL性能分析工具汇总!专治MySQL性能瓶颈
  3. Office文档在线预览接口服务器
  4. git log 提交日志及图形化显示
  5. java 日志技术汇总(log4j , Commons-logging,.....)
  6. poj3233(矩阵快速幂的和)
  7. Spring Cloud Stream与RabbitMQ 生产者和消费者位于同一个应用服务
  8. Python中报错Invalid return character or leading space in header: Cookie
  9. IDEA使用Build Artifacts进行项目打包时,发现没有可供打包的项目
  10. andriod studio在缩小图片_画图教室|有哪些办法可以有效缩小作品集的pdf文件大小?...