Google 的开源技术protobuf 简介与例子

本文来自CSDN博客:http://blog.csdn.NET/program_think/archive/2009/05/31/4229773.aspxphp

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

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

  ★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结构,用来描述通信协议中的结构化数据。该文件的内容大体以下:数据结构

 

--------------------------------socket

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

相关文章
相关标签/搜索