1、前言protobuf是Google开源的一种混合语言数据标准,已被各种互联网项目大量使用。其最大的特点是数据格式拥有极高的压缩比,这在移动互联时代是极具价值的(因为移动网络流量到目前为止还是相当昂贵的),如果你的APP能比竞品更省流量,无疑这也将成为您产品的亮点之一。现在,尤其IM、消息推送这类应用中,protobuf的应用更是非常广泛,基于它的优秀表现,微信和手机QQ这样的主流IM应用也早已在使用它。 现在随着WebSocket协议的越来越成熟,浏览器支持的越来越好,Web端的即时通讯应用也逐渐拥有了真正的“实时”能力,相关的技术和应用也是层出不穷,而protobuf也同样可以用在WebSocket的通信中。而且目前比较活跃的WebSocket开源方案中,都是用NodeJS实现的,比如:socket.io和sockjs都是如此,因而本文介绍protobuf在NodeJS上的使用,也恰是时候。 2、参考资料《Protobuf通信协议详解:代码演示、详细原理介绍等》 《强列建议将Protobuf作为你的即时通讯应用数据传输格式》 《全方位评测:Protobuf性能到底有没有比JSON快5倍?》 《Protobuf 官方开发者指南(中文译版)》 《Socket.IO介绍:支持WebSocket、用于WEB端的即时通讯的框架》 3、protobuf是个什么鬼?Protocol Buffer(下文简称protobuf)是Google提供的一种数据序列化协议,下面是我从网上找到的Google官方对protobuf的定义: Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。 道理我们都懂,然后并没有什么卵用,看完上面这段定义,对于protobuf是什么我还是一脸懵逼。 4、NodeJS开发者为何要跟Protocol Buffer打交道作为JavaScript开发者,对我们最友好的数据序列化协议当然是大名鼎鼎的JSON啦!我们本能的会想protobuf是什么鬼?还我JSON! 这就要说到protobuf的历史了。 Protobuf由Google出品,08年的时候Google把这个项目开源了,官方支持C++,Java,C#,Go和Python五种语言,但是由于其设计得很简单,所以衍生出很多第三方的支持,基本上常用的PHP,C,Actoin Script,Javascript,Perl等多种语言都已有第三方的库。 由于protobuf协议相较于之前流行的XML更加的简洁高效(后面会提到这是为什么),因此许多后台接口都是基于protobuf定制的数据序列化协议。而作为NodeJS开发者,跟C++或JAVA编写的后台服务接口打交道那是家常便饭的事儿,因此我们很有必要掌握protobuf协议。 为什么说使用使用类似protobuf的二进制协议通信更好呢?
在大公司,最重要的就是优化效率、节省成本,因此二进制协议明显优于http这样的文本协议。 下面举两个简单的例子,应该有助于我们理解protobuf。 5、选择支持protobuf的NodeJS第三方模块当前在Github上比较热门的支持protobuf的NodeJS第三方模块有如下3个: 根据star数和文档完善程度两方面综合考虑,我们决定选择protobuf.js(后面2个的地址:Google protobuf js、protocol-buffers)。 6、使用 Protobuf 和NodeJS开发一个简单的例子我打算使用 Protobuf 和NodeJS开发一个十分简单的例子程序。该程序由两部分组成:第一部分被称为 Writer,第二部分叫做 Reader。 Writer 负责将一些结构化的数据写入一个磁盘文件,Reader 则负责从该磁盘文件中读取结构化数据并打印到屏幕上。 准备用于演示的结构化数据是 HelloWorld,它包含两个基本数据:
1书写.proto文件首先我们需要编写一个 proto 文件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构化数据被称为 Message。proto 文件非常类似 java 或者 C 语言的数据定义。代码清单 1 显示了例子应用中的 proto 文件内容。 清单 1. proto 文件:
一个比较好的习惯是认真对待 proto 文件的文件名。比如将命名规则定于如下:
在上例中,package 名字叫做 lm,定义了一个消息 helloworld,该消息有三个成员,类型为 int32 的 id,另一个为类型为 string 的成员 str。opt 是一个可选的成员,即消息中可以不包含该成员。1、2、3这几个数字是这三个字段的唯一标识符,这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。 2编译 .proto 文件我们可以使用protobuf.js提供的命令行工具来编译 .proto 文件。 用法:
重点关注- -target就好,由于我们是在Node环境中使用,因此选择生成符合commonjs规范的文件。 命令如下:
得到编译后的符合commonjs规范的js文件:
3编写 Writer
4编写Reader
5运行结果由于我们没有在Writer中给可选字段opt字段赋值,因此Reader读出来的opt字段值为null。 这个例子本身并无意义,但只要您稍加修改就可以将它变成更加有用的程序。比如将磁盘替换为网络 socket,那么就可以实现基于网络的数据交换任务。而存储和交换正是 Protobuf 最有效的应用领域。 7、使用 Protobuf 和NodeJS实现基于网络数据交换的例子俗话说得好:“世界上没有什么技术问题是不能用一个helloworld的栗子解释清楚的,如果不行,那就用两个!” 在这个栗子中,我们来实现基于网络的数据交换任务。 1编写.protocover.helloworld.proto文件:
2编写client一般情况下,使用 Protobuf 的人们都会先写好 .proto 文件,再用 Protobuf 编译器生成目标语言所需要的源代码文件。将这些生成的代码和应用程序一起编译。 可是在某些情况下,人们无法预先知道 .proto 文件,他们需要动态处理一些未知的 .proto 文件。比如一个通用的消息转发中间件,它不可能预知需要处理怎样的消息。这需要动态编译 .proto 文件,并使用其中的 Message。 我们这里决定利用protobuf文件可以动态编译的特性,在代码中直接读取proto文件,动态生成我们需要的commonjs模块。 client.js:
3书写serverserver.js:
4运行结果8、其他高级特性1嵌套Message
在 Message Person 中,定义了嵌套消息 PhoneNumber,并用来定义 Person 消息中的 phone 域。这使得人们可以定义更加复杂的数据结构。 2Import Message在一个 .proto 文件中,还可以用 Import 关键字引入在其他 .proto 文件中定义的消息,这可以称做 Import Message,或者 Dependency Message。 比如下例:
其中 ,common.info_header定义在common.header包内。 Import Message 的用处主要在于提供了方便的代码管理机制,类似 C 语言中的头文件。您可以将一些公用的 Message 定义在一个 package 中,然后在别的 .proto 文件中引入该 package,进而使用其中的消息定义。 Google Protocol Buffer 可以很好地支持嵌套 Message 和引入 Message,从而让定义复杂的数据结构的工作变得非常轻松愉快。 9、总结一下Protobuf1优点简单说来 Protobuf 的主要优点就是:简洁,快。 为什么这么说呢? 【简洁】: 因为Protocol Buffer 信息的表示非常紧凑,这意味着消息的体积减少,自然需要更少的资源。比如网络上传输的字节数更少,需要的 IO 更少等,从而提高性能。 对于代码清单 1 中的消息,用 Protobuf 序列化后的字节序列为:
而如果用 XML,则类似这样:
一共 55 个字节,这些奇怪的数字需要稍微解释一下,其含义用 ASCII 表示如下:
我相信与XML一样同为文本序列化协议的JSON也不会好到哪里去。 【快】: 首先我们来了解一下 XML 的封解包过程。XML 需要从文件中读取出字符串,再转换为 XML 文档对象结构模型。之后,再从 XML 文档对象结构模型中读取指定节点的字符串,最后再将这个字符串转换成指定类型的变量。这个过程非常复杂,其中将 XML 文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。 反观 Protobuf,它只需要简单地将一个二进制序列,按照指定的格式读取到编程语言对应的结构类型中就可以了。而消息的 decoding 过程也可以通过几个位移操作组成的表达式计算即可完成。速度非常快。 2缺点作为二进制的序列化协议,人眼不可读! (原文链接:点此进入) |