手把手教你在 laravel 中使用 protobuf

好久之前做的業務了,網上涉及到 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.編寫代碼使用

簡單寫一個序列化的代碼,反序列化的代碼,你可以按需修改,需要注意的是,如果你做的也剛好是這個業務的話,因爲版本不兼容的問題,我們用v ersion 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');
        $binaryString = $idItem->serializeToString();
        // 手動拼前綴,不需要可以去掉
        $prefix = "\n\x19\x10\x00";
        $binaryString = $prefix . $binaryString;
        return $binaryString;
    }
    
    /**
     * 獲取反序列的一行
     * @param $line
     * @return string
     * @throws \Exception
     */
    public function decodeOneLine($line)
    {
        $item = new IdItem();
        $item->mergeFromString($line);
        return $item->getId();
    }
    
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章