5

友情提示

如果是跟我一样,是头条人群包的序列化问题,请出门右转>>>关于头条人群包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(二进制)缩写/字符解释
0x0a0000 1010LF (NL line feed, new line)换行键
0x190001 1001EM (end of medium)媒介结束
0x100001 0000DLE (data link escape)数据链路转义
0x000000 0000NUL(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还有其他的优化)。

最后,原创不易,转载请注明出处啊~


tfzh
231 声望17 粉丝

code what u love & love what u code