好久之前做的业务了,网上涉及到 laravel 使用 protobuf 的文章少的可怜,自己看了很多相关的文章,总结出来的用法,应该会有不少人需要
一、protobuf 简单介绍
Protobuf 是 Google 公司内部的混合语言数据标准,
是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
二、在 laravel 中封装使用 protobuf
PHP详解Protobuf的使用,工作实际业务是对接巨量引擎的 API 用到了,遇到了一些坑,估计大家也都会遇到,如果是一样的业务,请直接看最后的附加内容
1.Linux安装 protobuf 命令
安装此命令是为了能够解析指令,生成对应的文件
//获取 3.x 版本
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.12.0/protobuf-php-3.12.0.tar.gz
//解压代码
tar zxvf protobuf-php-3.12.0.tar.gz
//进入目录
cd protobuf-php-3.12.0
// 检查环境-编译安装
./configure --prefix=/usr/local/protobuf
sudo make
sudo make install
// 设置全局
export PATH=/usr/local/protobuf/bin:$PATH
// 测试是否安装成功
protoc --version
2. composer 安装包依赖
切换 composer 仓库源:
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
安装包
composer require google/protobuf:^3.3
因为 PHP 的composer 只支持了 protobuf 3,所以我们没办法去使用 version 2,因此,在对接 version 2 的验证时,会有一些兼容的问题。下面我会说明,两个版本之间的差别可以简单看一下这篇文章:
protobuf 2 和 3的区别
3.laravel 生成所需文件
我基于巨量引擎的对接API为例,在上传人群包文件时要求我们对文件进行序列化操作,如下图所示:
这是头条提供的上传格式 (version 2)
package toutiao.dmp;
option java_outer_classname = "DmpDataProto";
message DmpData { //上传文件每行一个base64编码的字符串,每个字符串包含一个完整的DmpData消息二进制字节串
repeated IdItem idList = 1; // 每行数据包含的idList大小不能超过10000
}
message IdItem {
optional uint32 timestamp = 1; //若不设置,默认以上传文件的创建时间为此条记录的创建时间
required DataType dataType = 2; //指定此id的类型,如IMEI、IDFA等
required string id = 3; //根据dataType字段的类型,放置对应类型的id的字符串,需要小写
repeated string tags = 4; //标识此id的业务标签字符串
enum DataType {
IMEI = 0;
IDFA = 1;
UID = 2;
IMEI_MD5 = 4;
IDFA_MD5 = 5;
MOBILE_HASH_SHA256 = 6;
OAID = 7;
OAID_MD5 = 8;
}
}
因为给的是 protobuff 2 的格式,所以我们需要改成 protobuff 3 的,修改后的结果如下:
syntax = "proto3";
package toutiao.dmp;
option java_outer_classname = "DmpDataProto";
message DmpData { //上传文件每行一个base64编码的字符串,每个字符串包含一个完整的DmpData消息二进制字节串
repeated IdItem idList = 1; // 每行数据包含的idList大小不能超过10000
}
message IdItem {
uint32 timestamp = 1; //若不设置,默认以上传文件的创建时间为此条记录的创建时间
string id = 3; //根据dataType字段的类型,放置对应类型的id的字符串,需要小写
string tags = 4; //标识此id的业务标签字符串
DataType dataType = 2; //指定此id的类型,如IMEI、IDFA等
enum DataType {
IMEI = 0;
IDFA = 1;
UID = 2;
IMEI_MD5 = 4;
IDFA_MD5 = 5;
MOBILE_HASH_SHA256 = 6;
OAID = 7;
}
}
在根目录下(任意目录下其实都可以)新建以下文件夹,用于存放proto 文件 和 生成的文件
在上级目录执行下列语句(就是 在 protobuf的上级目录):
protoc --php_out="protobuf/compile" "protobuf/protos/DmpDataProto.proto"
生成的结果如下:
├── compile
│ ├── GPBMetadata
│ │ └── Protobuf
│ │ └── Protos
│ │ └── DmpDataProto.php
│ └── Toutiao
│ └── Dmp
│ ├── DmpData.php
│ ├── IdItem_DataType.php
│ └── IdItem.php
└── protos
└── DmpDataProto.proto
4.自动加载生成的文件
如果框架没有加载我们自定义的目录文件,那么我们需要手动配置autoload, 在 composer.json 中的 autoload 下的 psr4 加上两个命名空间:
"Toutiao\\": "protobuf/compile/Toutiao",
"GPBMetadata\\Protobuf\\Protos\\": "protobuf/compile/GPBMetadata/Protobuf/Protos"
5.编写代码使用
简单写一个序列化的代码,反序列化的代码,你可以按需修改,需要注意的是,如果你做的也刚好是这个业务的话,因为版本不兼容的问题,我们用version 3 序列化的文件,version 2 是解不出来的,下面是 version 3 的使用,version 2的写法和做法在后面解释
/**
* 获取序列化的一行
* @param $id
* @return string
*/
public function getFormatLine($line)
{
$idItem = new IdItem();
$idItem->setDataType(IdItem_DataType::IMEI);
$idItem->setId(strtolower($line));
$idItem->setTags('IMEI');
return $idItem->serializeToString();
}
/**
* 获取反序列的一行
* @param $line
* @return string
* @throws \Exception
*/
public function decodeOneLine($line)
{
$item = new IdItem();
$item->mergeFromString($line);
return $item->getId();
}
6.附加
如果你做的业务跟我一样,上面的方法是没办法通过的。因为 通过 protobuf 3 序列化的数据是无法通过 protobuf2 反序列化的,因此,如果对接方是固定死了使用 protobuf 2, 那么我们只能委屈求全了,既然官方不提供,那么就找第三方的。
跟上面的一样,我们需要安装 protobuf 2.6 的命令,其实跟 3 的差不多
wget https://github.com/protocolbuffers/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
然后我们安装PHP对应的拓展,这里我们不再需要使用 composer 的包了,本质上来说,用拓展序列化的性能更高
wget https://github.com/allegro/php-protobuf/archive/master.zip
unzip master.zip
cd php-protobuf-master
phpize
./configure
make && make install
//然后在php.ini里面加一下extension = protobuf.so,再重启php-fpm
为了支持生成对应类库文件,需要进入到php-protobuf-master目录,执行下列命令
curl -s http://getcomposer.org/installer | php
php composer.phar install
php ./php-protobuf-master/protoc-gen-php.php DmpDataProto.proto
然后把生成的文件,放到你要放的项目文件里。和上面一样,补上对应的自动加载。
然后就是代码的使用,代码上稍微有些许不同
public function getFormatLine($id)
{
$dmpData = new DmpData();
$idItem = new IdItem();
$idItem->setDataType(IdItem_DataType::IMEI);
$idItem->setId(strtolower($id));
$idItem->appendTags('IMEI');
$idItem->setTimestamp(time());
$dmpData->appendIdList($idItem);
return $dmpData->serializeToString();
}
解析出来和使用 Python 脚本跑出来的结果是一样。