友情提示
如果是跟我一样,是头条人群包的序列化问题,请出门右转>>>关于头条人群包protobuf格式的php(python)解决方案
------------------我是没有感情的分割线------------------
服务器环境Ubuntu 18.04.5 LTS
PHP7.2.24
安装protoc
1.获取v3.13.0.1(截止2020.10.14)
wget https://codeload.github.com/protocolbuffers/protobuf/tar.gz/v3.13.0.1
2.解压
tar zxvf v3.13.0.1
cd protobuf-3.13.0.1
1.生成 configure 脚本;
./autogen.sh
2.编译安装
./configure --prefix=/usr/local/protobuf
make && make install
3.设置全局
export PATH=/usr/local/protobuf/bin:$PATH
4.检查安装成功
protoc --version
出现`libprotoc 3.13.0`即安装成功
安装php-protobuf拓展
pecl install protobuf
接下来,将
`extension=protobuf.so`添加到 `php.ini` 文件(例如 `/etc/php/7.2/fpm/php.ini`)中。
查看php.ini位置
1.cli命令行 php --ini
2.phpinfo();
3.ps -ef | grep php
在项目根目录
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
composer
composer require google/protobuf:^3.3
在composer.json
配置
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
+"Toutiao\\":"protobuf/compile/Toutiao",
+"GPBMetadata\\":"protobuf/compile/GPBMetadata/Protobuf/Protos"
}
},
生成优化的自动加载文件
composer dump-autoload
使用
//获取序列化的一行
public function decodeOneLine($line)
{
$idItem = new \Toutiao\Dmp\IdItem();
$idItem->setDataType(\Toutiao\Dmp\IdItem_DataType::IMEI);
$idItem->setId(strtolower($line));
$idItem->setTags('IMEI');
$binaryString = $idItem->serializeToString();
return $binaryString;
}
//获取反序列化的一行
public function decodeOneLine($line)
{
$item = new \Toutiao\Dmp\IdItem();
$item->mergeFromString($line);
return $item->getId();
}
主要为为了上传头条dmp包,但是头条是protobuf2,emmm明天再看吧
未完待续
ok
继续分享
我在百度Google搜了不少文章看,其中有篇博文写道:
博文地址
因为版本不兼容的问题,我们用version3 序列化的文件,version2 是解不出来的,通过实验发现每次都是少了一个前缀,因此我在下面的代码手动带上了这个前缀,头条才让我过。如果你不需要可以去掉。
// 手动拼前缀,不需要可以去掉
$prefix = "\n\x19\x10\x00";
$binaryString = $prefix . $binaryString;
我对比了下version3和2的差异
(重装protobuf2的过程与上方比较类似,此处不赘述了)
version3的结果:
root@ubuntu:# hexdump -C test.bin
00000000 1a 0f 38 36 30 37 31 34 30 34 31 39 31 35 36 30 |..86071404191560|
00000010 39 22 04 49 4d 45 49 |9".IMEI|
00000017
version2的结果:
root@ubuntu:# hexdump -C testV2.bin
00000000 0a 19 10 00 1a 0f 38 36 30 37 31 34 30 34 31 39 |......8607140419|
00000010 31 35 36 30 39 22 04 49 4d 45 49 |15609".IMEI|
0000001b
果然如博主所言,多了如下这四个字符
Hex(16进制) | Bin(二进制) | 缩写/字符 | 解释 |
---|---|---|---|
0x0a | 0000 1010 | LF (NL line feed, new line) | 换行键 |
0x19 | 0001 1001 | EM (end of medium) | 媒介结束 |
0x10 | 0001 0000 | DLE (data link escape) | 数据链路转义 |
0x00 | 0000 0000 | NUL(null) | 空字符 |
~~至此,完成。
使用V3版本转化成V2的完整代码如下:~~
ok,实际使用过程中呢,发现了新的问题,就是IMEI类型这么搞是没有问题的,但是IDFA/IMEI_MD5,反正只要不是IMEI,都是有问题的,需要拼接不同的前缀,因此,我觉得这样子是不靠谱的,虽然PHP可以序列化,以及反序列化,但是V3和V2序列化后,头条那边都是不认的,是反序列化失败,然后提工单回复又很慢,最后,只好使用pytohon序列化,写个脚本,PHP去调用这个脚本,然后头条成功接受(因为头条后端这块就是py吧)
这里贴一下py脚本代码,hex打印的部分也留着了,这个是当时对比V3/V2 以及PHP生成的二进制做对比用的
from __future__ import print_function
import DmpDataProtoV2_pb2
import os,sys
import time
import base64
dmp_data = DmpDataProtoV2_pb2.DmpData()
id_item1 = dmp_data.idList.add()
#dtype = 'IDFA'
dtype = sys.argv[1]
dev_id = sys.argv[2]
id_item1.dataType = getattr(DmpDataProtoV2_pb2.IdItem,dtype)
#id_item1.dataType = DmpDataProtoV2_pb2.IdItem.IDFA
id_item1.id = str.lower(dev_id)
id_item1.tags.append(dtype)
# id_item1.timestamp = int(time.time())
binary_string = dmp_data.SerializeToString()
#print(binary_string)
#print "nHex = %r" %(binary_string)
#t = bin(binary_string)
s = base64.b64encode(binary_string)
print (s,end='')
//获取序列化的一行
public function decodeOneLine($line, $v2 = false)
{
$idItem = new \Toutiao\Dmp\IdItem();
$idItem->setDataType(\Toutiao\Dmp\IdItem_DataType::IMEI);
$idItem->setId(strtolower($line));
$idItem->setTags('IMEI');
$binaryString = $idItem->serializeToString();
if ($v2) {
// 手动拼前缀,转化为V2,不需要可以不传V2参数
$prefix = "\n\x19\x10\x00";
$binaryString = $prefix . $binaryString;
}
return $binaryString;
}
总结:个人理解还是很浅薄的,只管感觉protobuf像是将key-value格式的json,去掉了key,只传输数据,而key在数据收发端约定好(当然protobuf还有其他的优化)。
最后,原创不易,转载请注明出处啊~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。