前言

年轻之时,刚接触数据库就有一直思考一个问题:要是数据量巨大对于数据的存储以及解读有没有什么神器可以助我一臂之力。长大后知道了,原来google家有一开源神器叫protobuf。刚知道的时候俺还是属于懵逼状态。这是啥?有什么好处?要怎么安装?要怎么用?,这篇文章主要就回答这几个问题。

基于自己也是查过很多的资料,大多资料都是copy来copy去,无可奈何,只好自己动手总结一下。

protobuf是什么,怎么来的?

简单的说就是干和xml一样的事,把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式等场合。

专业的解答:
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

有同学就问了,那不是已经有xml了还造轮子搞个protobuf干啥?。

根本原因还是:xml性能不好啊。

1.时间维度:xml格式化(序列化)的开销倒还好;但是XML解析(反序列化)的开销就呵呵了。
2.空间维度:XML格式为了有较好的可读性,引入了一些冗余的文本信息,所以空间开销也不是太好。

这对于大牛众多的google不能忍啊。必须搞一个不管是性能还是都优于xml的。

protobuf有什么好处?

1.对于数据结构的序列化反序列化等性能均优于xml
2.代码生成机制,使用起来十分方便(后续会提到)
3.明星项目有使用:据了解 google、新浪、美拍等均有使用protobuf,所以搬到项目使用肯定不会错。

protobuf如何安装?

本文所写的安装方法,是根据当前最新的安装包进行安装讲解的

准备资源:
protobuf-2.6.1安装包
php的protobuf扩展包

安装protobuf

wget https://github/google/protobuf/releases/download/v2.6.1/protobuf-2.6.1.tar.gz

tar zxvf protobuf-2.6.1.tar.gz

cd protobuf-2.6.1/

./configure --prefix=/usr/local/protobuf

make && make install

export PATH=/usr/local/protobuf/bin:$PATH

protoc --version

注意此处的包不能乱下载网上现成的。必须要大于2.5版本的。原因:最新版的protobuf代码生成机制要求必须大于2.5版本

安装php的protobuf扩展

wget https://github/allegro/php-protobuf/archive/master.zip

unzip master.zip

cd php-protobuf-master

yum install php-devel(安装依赖)

phpize

./configure --with-php-config=/usr/local/php/bin/php-config 

make && make install

//然后在php.ini里面加一下extension = "protobuf.so",再重启php与nginx即可。

安装composer

cd /path/to/you/../php-protobuf-master (存放php-protobuf-master的文件夹)

curl -s http://getcomposer/installer | php

php composer.phar install

protobuf如何使用?

1.代码生成机制:生成proto文件

vim cbstest.proto
message Person {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;
    optional double money = 4;
}

然后执行

php ./php-protobuf-master/protoc-gen-php.php cbstest.proto

然后就会形成一个 Person.php 的文件。

<?php
/**
 * Auto generated from test.proto at 2016-09-06 21:37:46
 */

namespace {
/**
 * Person message
 */
class Person extends \ProtobufMessage
{
    /* Field index constants */
    const NAME = 1;
    const ID = 2;
    const EMAIL = 3;
    const MONEY = 4;

    /* @var array Field descriptors */
    protected static $fields = array(
        self::NAME => array(
            'name' => 'name',
            'required' => true,
            'type' => \ProtobufMessage::PB_TYPE_STRING,
        ),
...省略...
}

这里会继承ProtobufMessage类,它是在protobuf.so中自动加载的

php实战的用法

对于俺这样非大神的人还是更关注实际的用法,如下:

$foo = new Person();
$foo->setName('hellojammy');
$foo->setId(2);
$foo->setEmail('helloxxx@foxmail');
$foo->setMoney(1988894.995);
$packed = $foo->serializeToString();

以上packed就是序列化后的结果,可直接存于数据库中。


$p = new Person();
$p->parseFromString($packed);
echo $p->getName() ."\n";
echo $p->getEmail() ."\n";
echo $p->getMoney() ."\n";
echo $p->getId() . "\n";

以上用于数据的反序列化。

有同学就问了,这也叫实战的例子?看过去好low,那是由于没有封装成函数的缘故。函数版如下,可直接添加到生成的文件的类底下。

public function encode($data){
    $fields = $this->fields();
    foreach ($fields as $key => $_item) {
        $field_name = $_item['name'];
        if(isset($data[$field_name])) {
            $this->set($key, $data[$field_name]);
        }
    }

    try {
        $packed = $this->serializeToString();
    } catch (\Exception $ex) {
        $data['fields'] = $fields;
        $data['error']  = $ex->getMessage();
        writeLog($data, 'encode_new_pb_failed');
        $packed = '';
    }

    return $packed;
}

public function decode($packed){
    try {
        $this->parseFromString($packed);

    } catch (\Exception $ex) {
        writeLog($ex->getMessage(), 'decode_new_pb_failed');
    }

    $data = array();
    $fields = $this->fields();
    foreach ($fields as $key => $_item) {
        $field_name = $_item['name'];
        $data[$field_name] = $this->get($key);
    }

    return $data;
}

从此可以愉快的使用protobuf了。

附录:

protobuf的githab
protobuf的版本资源

如果你觉得有收获~可以关注我的公众号【咖啡色的羊驼】~第一时间收到我的分享和知识梳理~

更多推荐

一看就懂系列之 protobuf的php版的教程