写这篇文章的原因是最近在关注RPC框架序列化的一些原理。可是在安装Protobuf的时候,发现网上的教程都太老了,加上目前Protobuf官方已经支持PHP了,再也不须要使用第三方插件了。php
在PRC框架中,数据的传输发生在客户端和服务端,而咱们知道基于TCP协议最终传输的是二进制的0/1序列。因此,基于TCP传输协议的RPC服务天然也须要将数据结构转换成二进制,和二进制转换成数据结构的功能。因此,原则上,基于网络的数据传输只能传输二进制表示的字符串java
序列化:将数据结构或对象转换成二进制串的过程 反序列化:将在序列化过程当中所生成的二进制串转换成数据结构或者对象的过程
可是,传输的二进制序列是彻底没有意义的,除非有一套解析二进制串的协议。没错,这个协议能够就是目前咱们你们熟知的xml,json协议。固然。除了这二者,还有其余的的序列化和反序列化协议。python
XML是一种经常使用的序列化和反序列化协议,具备跨机器,跨语言等优势。XML历史悠久,其1.0版本早在1998年就造成标准,并被普遍使用至今。XML的格式以下git
<note> <to>George</to> <from>John</from> <msg>Don't forget the meeting!</msg> </note>
能够看出这种序列化协议的优势是可读性和易调试行。可是这种协议的缺点也很明显:额外空间开销大,序列化以后的数据量剧增。github
JSON是一种轻量级的数据交换格式。采用彻底独立于编程语言的文本格式来存储和表示数据。若是你跟浏览器Web应用打交道的话,那么JSON必定是应用最普遍的,它的数据格式以下apache
{ "to":"George", "from":"John", "msg":"Don't forget the meeting!" }
这种序列化协议有很大的优点:编程
因为天生的Web友好型,JSON天然而然成了AJAX数据传输的标准协议。JSON目前的使用很是普遍,可是,若是数据传输和响应时间有跟苛刻的要求,那么JSON可能性能仍是差点。json
Google Protocol Buffer( 简称 Protobuf) 是一种更轻便高效的结构化数据存储格式,能够用于结构化数据串行化,或者说序列化。它很适合作数据存储或 RPC数据交换格式。数据结构定义文件的格式以下:浏览器
syntax = "proto3"; package config; message MailConfig{ string to = 1; string from = 2; string msg = 3; }
而后这个协议文件客户端和服务端都须要用到。这样,网络发送字节就不须要发送和数据无关的字节了。针对上面的数据结构,序列化成json的长度是63,而按照Protobuf协议序列化以后的长度是41。若是数据更多,效果更明显。网络
和Protobuf相似,可是Protobuf只作数据序列化的工做,而Thrift是一套完整的RPC框架解决方案。因为Thrift的序列化被嵌入到Thrift框架里面,Thrift框架自己并无透出序列化和反序列化接口,因此若是你使用Thrift框架,那么其实就使用了Thrift的序列化协议。
Thrift是一套完整的RPC解决方案,里面的协议也是一种基于二进制串的序列化协议,若是想要了解更多,这篇文章写得比较好:https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/
json和xml相信你们都比较熟悉,可是在RPC框架中不多使用这两种协议。因为Thrift是一套RPC解决方案,太过庞大,因此咱们分析专门解决序列化问题的Protobuf。因为笔者使用的是PHP,因此天然而然使用的是PHP版本的Protobuf。PHP有两种安装模式,第一是composer安装php包的形式,第二种是安装PHP扩展的形式。
要安装PHP版本的Protobuf,首先你得有PHP,因此首先安装PHP,笔者使用的PHP7,还须要一个工具protoc,你能够先不用管这个工具是干吗的,先安装。
工具包 | 版本 | 下载地址 |
---|---|---|
PHP | 7.1.8 | http://php.net/get/php-7.1.8.tar.gz/from/a/mirror |
proto | 3.0.0 | https://github.com/google/protobuf/releases/download/v3.3.0/protobuf-php-3.3.0.zip |
最后,笔者安装的PHP版本
[xxx@controller app]$ /usr/bin/php -v PHP 7.1.8 (cli) (built: Aug 16 2017 03:10:49) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
笔者安装的protoc版本(注意,有可能安装路径不一样)
[xxxx@controller app]$ /usr/local/bin/protoc --version libprotoc 3.3.0
Protobuf的安装咱们采用Composer形式进行安装。若是你尚未安装composer工具,运行下面的命令进行安装:
curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer
查看咱们安装的composer版本:
[xxxx@controller app]$ /usr/local/bin/composer --version Composer version 1.5.1 2017-08-09 16:07:22
准备工做结束。接下来,正式安装Protobuf。
咱们首先新建一个文件夹app
。而后在app
文件夹内新建composer.json
文件,文件内容以下:
{ "require":{ "google/protobuf": "^3.3" } }
保存以后,在app文件夹下执行composer install安装命令
[xxxx@controller app]$ /usr/local/bin/composer install Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Installing google/protobuf (v3.4.0): Downloading (100%)
安装完成以后,若是你看到的app
文件夹包含下面文件,表示安装成功
[xxxx@controller app]$ ls composer.json composer.lock vendor
上面的步骤安装完以后,咱们来看一下一个简单的例子,这个例子很简单,一个角色是Writer,一个角色是Reader。Writer向磁盘中写二进制字符串,Reader要读取二进制字符串中的信息。
咱们知道,二进制序列若是没有协议,根本不知道它表示的是什么,首先咱们须要编写一个 proto 文件,定义咱们程序中须要处理的结构化数据,在 protobuf的术语中,结构化数据被称为 Message。咱们这里定义一个mail的proto文件mail.proto
,这个文件仍是放在咱们的app
目录下, 文件内容以下:
syntax = "proto3"; package mail; message MailConfig{ string to = 1; string from = 2; string msg = 3; }
其中,syntax表示咱们使用的是proto3语法规则。packge的名字叫作mail,而后定义了一个名字为MailConfig的消息。这个消息有3个成员,string类型的to,string类型的from,string类型的msg。
还记得咱们下载安装的protoc
工具吗,这个玩意就是拿来编译.proto文件的,它能够将.proto文件编译成不少种目标语言,java,python,php等。这里咱们须要使用这个工具,将.proto文件生成目标PHP语言。
/usr/local/bin/protoc --php_out=. mail.proto
--php-out
表示生成目标语言存放位置,咱们就放在当前目录下,也就是app
文件夹下。若是生成目标语言成功,那么app
文件夹下的目录以下:
[xxxx@controller app]# ls composer.json composer.lock GPBMetadata Mail mail.proto vendor
在app中编写test.php
,文件内容以下:
<?php require_once('vendor/autoload.php'); require_once('GPBMetadata/Mail.php'); require_once('Mail/MailConfig.php'); /** * Writer写数据,Protobuf抽象成调用相关set函数便可 */ $foo = new \Mail\MailConfig(); $foo->setTo("George"); $foo->setFrom("John"); $foo->setMsg("Don't forget the meeting!"); $packed = $foo->serializeToString();//这里你也能够选择serializeToJsonString序列化成JSON //Reader读数据,Protobuf抽象成调用相关get函数便可 $res = new \Mail\MailConfig(); $res->mergeFromString($packed); $jsonArr = [ "to"=> $res->getTo(), "from"=> $res->getFrom(), "msg"=> $res->getMsg(), ]; var_dump($jsonArr);
最终程序的输出:
[xxxx@controller app]# php test.php array(3) { ["to"]=> string(6) "George" ["from"]=> string(4) "John" ["msg"]=> string(25) "Don't forget the meeting!" }
这就是一个典型的序列化和反序列化的例子,试想一下,若是咱们将序列化好的二进制串经过网络发送到另外一端,而另外一端再使用一样的.proto文件生成的目标语言解析。这将变得比json更加的高效。由于咱们能够传输更少的字节,反序列化速度也很是快。
到这里,确定有人以为效率仍是不够高,那么不要紧,protobuf有C语言扩展版的实现,若是你对效率有极致的要求,那么强烈建议使用C语言扩展版的protobuf。能够参考官方安装方式:https://github.com/google/protobuf/tree/master/php