Protobuf:高效数据结构化工具

protobuf

上一篇文章, 基于PHP的socket实现了简单的RPC功能。但是在开发过程中,由于TCP的长连接和频繁数据包传输, 一般都会对数据进行压缩。

Protobuf是Google开源的一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。具备以下特点:

  • 语言无关、平台无关,支持 Java、C++、Python、PHP等多种语言,支持多个平台;
  • 数据量小,解析速度快,比XML数据格式快20~100倍,比JSON快5~10倍;

在RPC开发中,多用Protobuf来作为数据交换协议,例如著名的rpc框架:gRPC

protobuf的使用很简单,按照一定的语法定义结构化的消息格式,然后用自带的编译工具(protoc),将自动生成对应语言相关的类.

Protoc安装

在使用protobuf之前,我们需要依据protobuf结构化语法定义结构化文件,再使用protoc将结构化文件生成各种语言的类。

Git:https://github.com/protocolbuffers/protobuf

Doc:https://developers.google.com/protocol-buffers/docs/proto3

先安装protoc:

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo apt-get install libtool 
# 下载安装包
wget https://github.com/protocolbuffers/protobuf/archive/master.zip
unzip master.zip
cd protobuf-master

./autogen.sh
# 配置编译参数
./configure --prefix=/usr/local/protobuf
# 编译
make
# 安装
sudo make install

可以将protobuf安装路径protoc所在的目录,加入到环境变量中,便于我们快速调用protoc命令。

1
2
# Protobuf
export PATH=$PATH:/usr/local/protobuf/bin

定义消息类型

以官方文档为例,先定义一个Person消息类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
syntax = "proto3";

message Person {
string name = 1;
int32 id = 2;
string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
string number = 1;
PhoneType type = 2;
}

PhoneNumber phone = 4;
}

protobuf

进行protoc编译,生成对应的php类:

1
protoc --php_out=./ person.proto

编译后目录结构:

protobuf

序列化/反序列化

我们需用composer引用google/protobuf库来调用protobuf:

编写composer.json:

1
2
3
4
5
6
7
8
9
10
{
"name": "php-protobuf",
"description": "PHP Protobuf Test",
"keywords": ["protobuf"],
"license": "MIT",
"type": "project",
"require":{
"google/protobuf":"3.9.0"
}
}

安装依赖包:composer install

protobuf

编写代码,测试序列化/反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require('./vendor/autoload.php');

require('./GPBMetadata/Person.php');
require('./Person/PhoneNumber.php');
require('./Person/PhoneType.php');
require('./Person.php');

$person = new Person();
$person->setId(1);
$person->setName("gitlib");

$phone = new \Person\PhoneNumber();
$phone->setNumber('123456');
$phone->setType(\Person\PhoneType::HOME);

$person->setPhone($phone);
$data = $person->serializeToString();

var_dump($data);

$person2 = new Person();
$person2->mergeFromString($data);
var_dump("id:".$person2->getId()." name:".$person2->getName()." phone num:".$person2->getPhone()->getNumber());

?>

运行结果:

protobuf


本文更新于:2019年6月,修改采用proto3和php7。

有用就打赏一下作者吧!