Google 开源技术protobuf 简介与样例

今天来介绍一下“Protocol Buffers ”(以下简称protobuf)这个玩意儿。本来俺在构思“生产者/消费者模式 ”系列的下一个帖子:关于生产者和消费者之间的数据传输格式。由于里面扯到了protobuf,想想干脆单独开一个帖子算了。

  ★protobuf是啥玩意儿?
  为了照顾从没听说过的同学,照例先来扫盲一把。
  首先,protobuf是一个开源 项 目(官方站点在“这里 ”),而且是后台很硬的开源项目。网上现有的大部分(至少80%)开源项目,要么是某人单干、要么是几个闲杂人等合伙搞。而protobuf则不然,它是 鼎鼎大名的Google公司开发出来,并且在Google内部久经考验的一个东东。由此可见,它的作者绝非一般闲杂人等可比。
  那这个听起来牛X的东东到底有啥用处捏?简单地说,这个东东干的事儿其实和XML 差不多,也就是把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式等场合。有同学可能心理犯嘀咕了:放着好好的XML不用,干嘛重新发明轮子啊?!先别急,后面俺自然会有说道。
  话说大约是08年7月,Google突然大发慈悲,把这个好东西贡献给了开源社区。这下,像俺这种喜欢捡现成的家伙可就有福啦!貌似喜欢 捡现成的家伙还蛮多滴,再加上 Google的号召力,开源后不到一年,protobuf的人气就已经很旺了。所以俺为了与时俱进,就单独开个帖子来忽悠一把。

  ★protobuf有啥特色?
  扫盲完了之后,就该聊一下技术 方面的话题了。由于这玩意儿发布的时间较短(未满周岁),所以俺接触的时间也不长。今天在此是先学现卖,列位看官多多包涵 :-)

  ◇性能好/效率高
  现在,俺就来说说Google公司为啥放着好端端的XML不用,非要另起炉灶,重新造轮子。一个根本的原因是XML性能不够好。
  先说时间开销:XML格式化(序列化)的开销倒还好;但是XML解析(反序列化)的开销就不敢恭维啦。俺之前经常碰到一些时间性能很敏感的场合,由于不堪忍受XML解析的速度,弃之如敝履。
  再来看空间开销:熟悉XML语法的同学应该知道,XML格式为了有较好的可读性,引入了一些冗余的文本信息。所以空间开销也不是太好(不过这点缺点,俺不常碰到)。
  由于Google公司赖以吹嘘的就是它的海量数据和海量处理能力。对于几十万、上百万机器的集群,动不动就是PB级的数据量,哪怕性能稍微提高 0.1% 也是相当可观滴。所以Google自然无法容忍XML在性能上的明显缺点。再加上Google从来就不缺造轮子的牛人,所以protobuf也就应运而生 了。
  Google对于性能的偏执,那可是出了名的。所以,俺对于Google搞出来protobuf是非常滴放心,性能上不敢说是最好,但肯定不会太差。

  ◇代码 生成机制
  除了性能好,代码生成机制是主要吸引俺的地方。为了说明这个代码生成机制,俺举个例子。
  比如有个电子商务的系统(假设用C++实现),其中的模块A需要发送大量的订单信息给模块B,通讯的方式使用socket。
假设订单包括如下属性:
--------------------------------
  时间:time(用整数表示)
  客户id:userid(用整数表示)
  交易金额:price(用浮点数表示)
  交易的描述:desc(用字符串表示)
--------------------------------
  如果使用protobuf实现,首先要写一个proto文件(不妨叫Order.proto),在该文件中添加一个名为"Order"的message结构,用来描述通讯协议中的结构化数据。该文件的内容大致如下:

 

--------------------------------

message Order
{
  required int32 time = 1;
  required int32 userid = 2;
  required float price = 3;
  optional string desc = 4;
}

--------------------------------

 


  然后,使用protobuf内置的编译器编译 该proto。由于本例子的模块是C++,你可以通过protobuf编译器的命令行参数(看“这里 ”),让它生成C++语言的“订单包装类”。(一般来说,一个message结构会生成一个包装类)
  然后你使用类似下面的代码来序列化/解析该订单包装类:


--------------------------------

// 发送方

Order order;
order.set_time(XXXX);
order.set_userid(123);
order.set_price(100.0f);
order.set_desc("a test order");

string sOrder;
order.SerailzeToString(&sOrder);

// 然后调用某种socket的通讯库把序列化之后的字符串发送出去
// ......

--------------------------------

// 接收方

string sOrder;
// 先通过网络通讯库接收到数据,存放到某字符串sOrder
// ......

Order order;
if(order.ParseFromString(sOrder))  // 解析该字符串
{
  cout << "userid:" << order.userid() << endl
          << "desc:" << order.desc() << endl;
}
else
{
  cerr << "parse error!" << endl;
}

--------------------------------

 


  有了这种代码生成机制,开发人员再也不用吭哧吭哧地编写那些协议解析的代码了(干这种活是典型的吃力不讨好)。
  万一将来需求发生变更,要求给订单再增加一个“状态”的属性,那只需要在Order.proto文件中增加一行代码。对于发送方(模块A),只要增加一行设置状态的代码;对于接收方(模块B)只要增加一行读取状态的代码。哇塞,简直太轻松了!
  另外,如果通讯双方使用不同的编程语言来实现,使用这种机制可以有效确保两边的模块对于协议的处理是一致的。
  顺便跑题一下。
  从某种意义上讲,可以把proto文件看成是描述通讯协议的规格说明书(或者叫接口规范)。这种伎俩其实老早就有了,搞过微软的COM编程或者接触过CORBA的同学,应该都能从中看到IDL(详细解释看“这里 ”)的影子。它们的思想是相通滴。

  ◇支持“向后兼容”和“向前兼容”
  还是拿刚才的例子来说事儿。为了叙述方便,俺把增加了“状态”属性的订单协议成为“新版本”;之前的叫“老版本”。
  所谓的“向后兼容”(backward compatible),就是说,当模块B升级了之后,它能够正确识别模块A发出的老版本的协议。由于老版本没有“状态”这个属性,在扩充协议时,可以考 虑把“状态”属性设置成非必填 的,或者给“状态”属性设置一个缺省值(如何设置缺省值,参见“这里 ”)。
  所谓的“向前兼容”(forward compatible),就是说,当模块A升级了之后,模块B能够正常识别模块A发出的新版本的协议。这时候,新增加的“状态”属性会被忽略。
  “向后兼容”和“向前兼容”有啥用捏?俺举个例子:当你维护一个很庞大的分布式系统时,由于你无法同时 升级所有 模块,为了保证在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼容”。

  ◇支持多种编程语言
  俺开博以来点评 的几个开源项目(比如“Sqlite ”、“cURL ”),都是支持很多种 编程语言滴,这次的protobuf也不例外。在Google官方发布的源代码中包含了C++、Java 、Python三种语言(正好也是俺最常用的三种,真爽)。如果你平时用的就是这三种语言之一,那就好办了。
  假如你想把protobuf用于其它语言,咋办捏?由于Google一呼百应的号召力,开源社区对protobuf响应踊跃,近期冒出很多其它编程语言的版本(比如ActionScript、C#、Lisp、Erlang、Perl、PHP 、Ruby等),有些语言还同时搞出了多个开源的项目。具体细节可以参见“这里 ”。
  不过俺有义务提醒一下在座的各位同学。如果你考虑把protobuf用于上述这些语言,一定认真评估对应的开源库。因为这些开源库不是Google官方提供的、而且出来的时间还不长。所以,它们的质量、性能等方面可能还有欠缺。

  ★protobuf有啥缺陷?
  前几天刚刚在“光环效应 ”的帖子里强调了“要同时评估优点和缺点”。所以俺最后再来批判一下这玩意儿的缺点。
  ◇应用 不够广
  由于protobuf刚公布没多久,相比XML而言,protobuf还属于初出茅庐。因此,在知名度、应用广度等方面都远不如XML。由于这个原因,假如你设计的系统需要提供若干对外的接口给第三方系统调用,俺奉劝你暂时不要考虑protobuf格式。
  ◇二进制格式导致可读性差
  为了提高性能,protobuf采用了二进制格式进行编码。这直接导致了可读性差的问题(严格地说,是没有可读性)。虽然protobuf提供了TextFormat这个工具类(文档在“这里 ”),但终究无法彻底解决此问题。
  可读性差的危害,俺再来举个例子。比如通讯双方如果出现问题,极易导致扯皮(都不承认自己有问题,都说是对方的错)。俺对付扯皮的一个简单方法 就是直接抓包并dump成log,能比较容易地看出错误在哪一方。但是protobuf的二进制格式,导致你抓包并直接dump出来的log难以看懂。
  ◇缺乏自描述
  一般来说,XML是自描述的,而protobuf格式则不是。给你一段二进制格式的协议内容,如果不配合相应的proto文件,那简直就像天书一般。
  由于“缺乏自描述”,再加上“二进制格式导致可读性差”。所以在配置文件方面,protobuf是肯定无法取代XML的地位滴。

  ★为什么俺会用上protobuf?
  俺自从前段时间接触了protobuf之后,就着手把俺负责的产品中的部分 数据传输协议替换成protobuf。可能有同学会问,和protobuf类似的东东也有不少,为啥独独相中protobuf捏?由于今天写的篇幅已经蛮 长了,俺卖个关子,把这个话题留到“生产者/消费者模式[5]:如何选择传输协议及格式?”。俺会在后续的这个帖子里对比各种五花八门的协议格式,并谈谈 俺的浅见。v

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE全局导航守卫、 请求、响应拦截器 的设置

文件设置参考地址&#xff1a;https://gitee.com/wang_yu5201314/headlines__news/tree/master/%E9%A1%B9%E7%9B%AE%E6%BA%90%E7%A0%81%E6%96%87%E4%BB%B6/src 文件夹 Router 文件夹 index.js 中设置 全局导航守卫 文件 mian.js 中设置 请求、响应拦截器 设置 请求、响应拦截器…

JRE System Library和 Referenced Libraries 的区别和来源

JRE System Library 安装jdk后&#xff0c;会有个目录叫做jrejre目录是核心类库&#xff0c;目录中装的是类库文件jre System Library顾名思义就表示系统类库文件 Referenced Libraries referenced libraries放的是你引用的jar包&#xff0c;这个不需要自己创建的&#xff0c;你…

ByteArray、16进制、字符串之间的转换

ByteArray、16进制、字符串之间的转换&#xff1a; package fengzi.convert {import flash.utils.ByteArray;public class ByteArrayTranslated{/**** 通过hax数据返回ByteArray* param hax 格式 "AA5A000100FF"***/public static functi…

js - (初中级)常见笔试面试题

1.用 js 实现一个深拷贝 2.用 js 写一个数组去重 3. 用 js 对字符串进行反转 4. 用 js 请求范围内的质数个数 5.用 js 求数组中出现最多的数及其出现次数

iOS 支付宝SDK接入详解

一&#xff0c;在支付宝开放平台下载支付宝SDK&#xff08;https://openhome.alipay.com/platform/document.htm#down&#xff09; https://doc.open.alipay.com/doc2/detail.htm?spma219a.7629140.0.0.HpDuWo&treeId54&articleId104509&docType1 二&#xff0c;添…

面试基本知识点

文章目录面-什么是SEO面 - cookie / localstorage / sessionstorage的区别面 - promise面试题面 - 柯里化函数面 - 函数节流面 - 函数防抖HTML / CSS 知识点1、讲讲盒模型&#xff08;蚂蚁金服 2019.03 招行信用卡 2019.04 美团 作业帮&#xff09;2、根据盒模型解释边距重叠&a…

Redis 热点key

压测报redis 热点问题 热点问题概述 产生原因 热点问题产生的原因大致有以下两种&#xff1a; 用户消费的数据远大于生产的数据&#xff08;热卖商品、热点新闻、热点评论、明星直播&#xff09;。 在日常工作生活中一些突发的的事件&#xff0c;例如&#xff1a;双十一期间某些…

移动IM开发那些事:技术选型和常见问题

最近在做一个iOS IM SDK&#xff0c;在内部试用的阶段&#xff0c;不断有兄弟部门或者合作伙伴过来问各种技术细节&#xff0c;所以统一写一篇文章记录&#xff0c;统一介绍下一个IM APP的方方面面&#xff0c;包括技术选型(包括通讯方式,网络连接方式,协议选择)和常见问题。 …

webstrom打开通过顶部浏览器打开网页,被跳转到默认主页

重新开始工作啦&#xff0c;希望以后认真一点&#xff0c;并把遇到的问题都记录下来&#xff0c;虽然是小小白&#xff0c;但能无意间帮助到别人就更开心了呀 通过webstrom打开本地的文件时&#xff0c;发现跳转到了默认主页上&#xff0c;吐槽下&#xff0c;有些主页真的超级流…

mockjs(接口服务代理)

mock官网&#xff1a;http://mockjs.com/ 一、搭建一个练习项目 1.利用vue的项目脚手架进行搭建 命令&#xff1a; vue create mock-demo 截图&#xff1a; 2.安装相关的依赖 命令&#xff1a; #使用 axios 发送 ajax npm install axios --save #使用 mock.js 产生随机数据…

MD5算法原理

MD5&#xff08;单向散列算法&#xff09; 的全称是Message-Digest Algorithm 5&#xff08;信息-摘要算法&#xff09;&#xff0c;经MD2、MD3和MD4发展而来。MD5算法的使用不需要支付任何版权费用。MD5功能&#xff1a;输入任意长度的信息&#xff0c;经过处理&#xff0c;输…

函数-函数进阶-装饰器流程分析

老王&#xff1a;算了&#xff0c;估计你也想不出来。。。学过嵌套函数没有&#xff1f; 你&#xff1a;yes&#xff0c;然后呢&#xff1f; 老王&#xff1a;想实现一开始你写的america login(america)不触发你函数的执行&#xff0c;只需要在这个login里面再定义一层函数&am…

制作手写签名

<!DOCTYPE html> <!-- saved from url(0056)http://hao2013.cn/canvas-special-master/brush/index.html --> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>签名板(支持移动…

python第五次作业——陈灵院

习题1&#xff1a;读入文件pmi_days.csv&#xff0c;完成以下操作&#xff1a;1.统计质量等级对应的天数&#xff0c;例如&#xff1a;优&#xff1a;5天良&#xff1a;3天中度污染&#xff1a;2天2.找出PMI2.5的最大值和最小值&#xff0c;分别指出是哪一天。 import csv impo…

iOS 二叉树相关算法实现

什么是二叉树&#xff1f; 在计算机科学中&#xff0c;二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”&#xff0c;左子树和右子树同时也是二叉树。二叉树的子树有左右之分&#xff0c;并且次序不能任意颠倒。二叉树是递归定义的&#xff0c;所…

vux 组件库首次使用安装

1、首先通过脚手架新建一个项目&#xff0c;过程略。 创建完项目后&#xff0c;在项目里安装vux&#xff0c; 通过命令 npm install vux --save 安装 2、安装vux-loader&#xff0c; 通过命令 npm install vux-loader --save-dev 安装&#xff08;vux文档没说明&#xff09; 3、…

@Component 和 @Bean 的区别

Spring帮助我们管理Bean分为两个部分&#xff0c;一个是注册Bean&#xff0c;一个装配Bean。完成这两个动作有三种方式&#xff0c;一种是使用自动配置的方式、一种是使用JavaConfig的方式&#xff0c;一种就是使用XML配置的方式。 Compent 作用就相当于 XML配置 Component pub…

js动态验证码获取

<!DOCTYPE html> <html lang"cn"> <head><meta charset"UTF-8"><title>短信验证码</title> </head> <body> <input type"number" id"tel" value"13303861063"> <…

Base64 算法原理,以及编码、解码【加密、解密】 介绍

Base64编码&#xff0c;是我们程序开发中经常使用到的编码方法。它是一种基于用64个可打印字符来表示二进制数据的表示方法。它通常用作存储、传输一些二进制数据编码方法&#xff01;也是MIME&#xff08;多用途互联网邮件扩展&#xff0c;主要用作电子邮件标准&#xff09;中…

js通过身份证获取年龄

// 获取用户的身份证号码let identityCard this.idNum.replace(/\s/g, "");//判断长度let len identityCard.length;//设置新的变量var strBirthday "";//根据长度获取年月日if (len 18) {strBirthday identityCard.substr(6, 4) "/" identi…