在 PHP 使用 Protobuf Buffers 有性能问题

栏目: 服务器 · 发布时间: 5年前

Protocol Buffers 是一种应用广泛的数据交换格式,内部服务间通信时常常用它做为数据传输格式,主要是因为较之 Json 它具有更好的性能(体现在空间占用更少、编解码更快)、数据结构定义更规范。 然而在 PHP 中使用 Protocol Buffers 却是一场灾难。

Protocol Buffers 初始化是很慢的

通过 protoc 编译出来的骨架代码本质上只是将 proto 文件编译成方便解析的中间文件格式,建立 PHP 层对应的类型系统,绝大部分的工作放到了运行时。

参考 php performance problem · Issue #4227 · protocolbuffers/protobuf

使用 XHProf 分析实际的请求,发现 40% 的时间(133 毫秒)花在了初始化上

使用的是 Protocol Buffers 的 PHP 实现,耗时的调用栈如下

 PaymentEvent::__construct 
 – GPBMetadata\Xllivensq::initOnce 
 -— Google\Protobuf\Internal\DescriptorPool::internalAddGeneratedFile 

C 扩展性能是 PHP 实现的 10 倍以上,主要是因为使用 C 扩展后底层对 Protocol Buffers 字节流的编、解码更快了。

Protocol Buffers 的 C 扩展不稳定

线上系统使用中发现内存和 CPU 占用过高。

Protocol Buffers 性能问题对 PHP 应用的影响是巨大的

其它语言(如:Go、C++)程序是常驻内存运行的,初始化 Protocol Buffers 的开销只付出一次,所以往往意识不到这个问题。

PHP 采用 FastCGI 模型,每次请求都要承担 Protocol Buffers 的开销,很快就会发现采用 Protocol Buffers 后接口响应时间变长,系统的吞吐率下降。

PHP 实现与 C 扩展的性能对比

  • Protocol Buffers C 扩展的安装

    sudo pecl install protobuf-3.7.0
  • Protocol Buffers C 扩展的启用与禁用

    php.ini 加入以下配置项启用 C 扩展

    extension=protobuf.so

    注释掉上面的配置项禁用 C 扩展。

  • 创建性能测试 PHP 项目

    进入项目目录

    创建 composer.json 文件,内容如下

    {
        "require": {
            "google/protobuf": "3.7.0"
        }
    }

    创建 index.php 文件,内容如下

    <?php
    $startTime = microtime(true);
    
    require __DIR__ . '/vendor/autoload.php';
    require __DIR__ . '/generated/GPBMetadata/Addressbook.php';
    require __DIR__ . '/generated/Tutorial/AddressBook.php';
    require __DIR__ . '/generated/Tutorial/Person.php';
    require __DIR__ . '/generated/Tutorial/Person/PhoneType.php';
    require __DIR__ . '/generated/Tutorial/Person/PhoneNumber.php';
    
    $addressBook = new Tutorial\AddressBook();
    
    $person = new Tutorial\Person();
    $person->setId(1);
    $person->setName("user1");
    $person->setEmail("[email protected]");
    $phone = new Tutorial\Person\PhoneNumber();
    $phone->setType(Tutorial\Person\PhoneType::HOME);
    $phone->setNumber("1000001");
    $person->getPhones()[] = $phone;
    $addressBook->getPeople()[] = $person;
    
    $person = new Tutorial\Person();
    $person->setId(2);
    $person->setName("user2");
    $person->setEmail("[email protected]");
    $phone = new Tutorial\Person\PhoneNumber();
    $phone->setType(Tutorial\Person\PhoneType::HOME);
    $phone->setNumber("2000002");
    $person->getPhones()[] = $phone;
    $addressBook->getPeople()[] = $person;
    
    $person = new Tutorial\Person();
    $person->setId(3);
    $person->setName("user3");
    $person->setEmail("[email protected]");
    $phone = new Tutorial\Person\PhoneNumber();
    $phone->setType(Tutorial\Person\PhoneType::HOME);
    $phone->setNumber("3000003");
    $person->getPhones()[] = $phone;
    $addressBook->getPeople()[] = $person;
    
    $data = $addressBook->serializeToString();
    
    $endTime = microtime(true);
    $runTime = ($endTime-$startTime)*1000 . ' ms';
    
    $class = new ReflectionClass('Google\Protobuf\Internal\DescriptorPool');
    if ($class->getFileName()) {
        echo "protobuf-php $runTime\n";
    } else {
        echo "protobuf-c   $runTime\n";
    }

    创建 Makefile 内容如下

    all: install generate benchmark
    
    install: vendor
    
    vendor: composer.json
        composer install
    
    generate: generated/GPBMetadata
    
    generated/GPBMetadata: addressbook.proto
        -mkdir generated
        protoc --php_out=generated addressbook.proto
    
    benchmark:
        php index.php | tee -a ./benchmark.log
    
    addressbook.proto:
        wget https://raw.githubusercontent.com/protocolbuffers/protobuf/master/examples/addressbook.proto -O ${CURDIR}/addressbook.proto
    
    clean:
        -rm -rf ${CURDIR}/vendor ${CURDIR}/generated
  • 运行性能测试

    make

    交替启用禁用 C 扩展的运行结果

    protobuf-c   2.335 ms
    protobuf-php 29.14 ms
    protobuf-c   2.443 ms
    protobuf-php 27.962 ms
    protobuf-c   2.238 ms
    protobuf-php 14.957 ms
    protobuf-c   2.221 ms
    protobuf-php 29.814 ms
    protobuf-c   2.295 ms
    protobuf-php 29.304 ms
    protobuf-c   2.435 ms
    protobuf-php 37.493 ms

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

超级连接者:破解新互联时代的成功密码

超级连接者:破解新互联时代的成功密码

伊桑•祖克曼(ETHAN ZUCKERMAN) / 林玮、张晨 / 浙江人民出版社 / 2018-8-1 / CNY 72.90

● 我们生活在一个互联互通的世界,我们需要辩证地看待某些事件,发现隐藏在背后的真相。着眼当下,看清彼此之间的联系,而非凭空幻想未来世界联系之紧密。数字世界主义要求我们承担起责任,让隐藏的联系变成现实。 ● 我们对世界的看法是局限的、不完整的、带有偏见的。如果我们想要改变从这个广阔的世界所获取的信息,我们需要做出结构性的改变。 ● 建立联系是一种新的力量。无论是在国家层面、企业层面还是个......一起来看看 《超级连接者:破解新互联时代的成功密码》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具