再来一次手牵手:php7.0 + protobuf
Posted 复杂美干货日记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再来一次手牵手:php7.0 + protobuf相关的知识,希望对你有一定的参考价值。
这两天在折腾 php。
php 作为世界世界上最好的编程语言,我不过是读研那会给导师做项目的时候撸过一年的代码,水平一般,只了解基本的语法,会进行简单的 php 后台开发,后来就没再用过了。
这次,是为了工作需要,在整理项目模块的时候,同事反映“之前的 php5.x 对 protobuf 的支持不是很好,解码出来的数据结构跟其它语言的对不上……”, 我当时的第一反应是:居然有这种事?至于具体是不是真的如此,我也懒得去重现。那是两年前的事,现在 php 都到 7.x ,protobuf 都到 3.5.1 了,时过境迁,IT 技术日新月异,或许问题早就解决了呢?
报着试一试的态度,我主动提出来调研下 “protobuf 在 php7.0 的用法”。
php
docker 里面现成的 php 镜像多的是,想要快的话,直接 run 一个 php 容器出来就可以,但是我想从零开始,体验下 Linux 配置 php 开发环境的流程,一步一个脚印,给人一种稳重的安全感。
所以,所以还是从一个裸体的 ubuntu 系统开始吧。
启动一个 ubuntu:16.04
容器:
docker run -d -P -it ubuntu:16.04 bash
先装 php 的,这里我选了 7.0 版本:
root@aae76b5e0ca4:/var/www/html# apt-get update
root@aae76b5e0ca4:/var/www/html# apt-get install php7.0
root@aae76b5e0ca4:/var/www/html# php -v
PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.22-0ubuntu0.16.04.1, Copyright (c) 1999-2017, by Zend Technologies
装个 nginx 服务器来驱动 php 脚本吧:
root@aae76b5e0ca4:/var/www/html# apt-get intall nginx
root@aae76b5e0ca4:/var/www/html# nginx -v
nginx version: nginx/1.10.3 (Ubuntu)
配置下 nginx,开启 php 解释器, 修改 /etc/nginx/sites-enabled/default
这部分如下:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php7.0-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# With php7.0-fpm:
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
启动 PHP 和 nginx 服务:
root@aae76b5e0ca4:/var/www/html# service php7.0-fpm start
root@aae76b5e0ca4:/var/www/html# service nginx start
* Starting nginx nginx [ OK ]
这样,php 运行环境就搭建好了,可以写个简单的 PHP 脚本 helloworld.php
来验证下,把它放在 nginx 默认的网站根目录 /var/www/html
中:
<?php
echo "Hello World!";
?>
现在就是见证奇迹的时刻:
root@aae76b5e0ca4:/var/www/html# curl 127.0.0.1/helloworld.php
Hello World!
只需几步就配置好了 php 运行环境,真是 Excited!
protobuf
php7 要使用 Google protobuf 需要安装两个东西:protoc 编译器
和 php 扩展库
。
protoc 编译器
到 https://github.com/google/protobuf/releases
下载 linux 平台的 protoc ,把 bin
和 include
里面的文件拷贝到 /usr/local
的对应目录,这样 protoc 就算安装完成了:
root@aae76b5e0ca4:/var/www/html# protoc --version
libprotoc 3.5.1
写个 test.proto
:
syntax = "proto3";
message PhoneNumber {
string number = 1;
int32 type = 2;
}
message Person {
string name = 1;
int32 id = 2;
string email = 3;
repeated PhoneNumber phone = 4;
double money = 5;
}
message AddressBook {
repeated Person person = 1;
}
这个 proto 文件是什么意思,相信用过 proto 的人都知道。
编译 proto 文件:
root@aae76b5e0ca4:/var/www/html# mkdir foo
root@aae76b5e0ca4:/var/www/html# protoc --php_out=foo test.proto
root@aae76b5e0ca4:/var/www/html# tree foo/
foo/
|-- AddressBook.php
|-- GPBMetadata
| `-- Test.php
|-- Person.php
`-- PhoneNumber.php
可以看到,protoc 编译出的结果里包含一个 三个 php 文件(对应 proto 文件里面声明的三种结构)和 一个 GPBMetadata
文件夹(下面的 Test.php
描述了 test.proto
文件的元信息)。
php 扩展库
protobuf 的 php 扩展 官方 提供了 c 动态库 和 php 包两种形式。
php 包形式我折腾了半天也不会用,烦死了。还是安装 c 动态库形式的扩展吧:
root@aae76b5e0ca4:/var/www/html# apt-get install php-pear php5-dev autoconf automake libtool make gcc
root@aae76b5e0ca4:/var/www/html# pecl install protobuf-3.5.1
...
Build process completed successfully
Installing '/usr/lib/php/20151012/protobuf.so'
install ok: channel://pecl.php.net/protobuf-3.5.1
configuration option "php_ini" is not set to php.ini location
You should add "extension=protobuf.so" to php.ini
提示安装成功,但还需要修改 php 的配置文件 /etc/php/7.0/fpm/php.ini
, 开启 protobuf 库支持:
;extension=php_xsl.dll
extension=protobuf.so
重启 php7.0-fpm 服务。
Dev
准备工作做好后,就可以正式进行 protobuf 解析了。
写个测试例子 test.php
:
<?php
require_once "foo/GPBMetadata/Test.php";
require_once "foo/AddressBook.php";
require_once "foo/Person.php";
require_once "foo/PhoneNumber.php";
$foo = new Person();
$foo->setName('hxz');
$foo->setId(2);
$foo->setEmail('notexist@foxmail.com');
$foo->setMoney(1988894.995);
$phone_num = new PhoneNumber();
$phone_num->setNumber('1351010xxxx');
$phone_num->setType(3);
$phone = array();
array_push($phone, $phone_num);
$foo->setPhone($phone);
$packed = $foo->serializeToString();
try {
$p = new Person();
$p->mergeFromString($packed);
echo "------------parsed-------\n";
echo $p->getName() ."\n";
echo $p->getEmail() ."\n";
echo $p->getMoney() ."\n";
echo $p->getId() . "\n";
echo $p->getPhone()[0]->getNumber() ."\n";
} catch (Exception $ex) {
die('Upss.. there is a bug in this example');
}
?>
访问 127.0.0.1/test.php
输出:
root@aae76b5e0ca4:/var/www/html# curl localhost/test.php
------------parsed-------
hxz
notexist@foxmail.com
1988894.995
2
1351010xxxx
看上去,protobuf 的 serializeToString
和 mergeFromString
都没有问题。
一切看上去都那么自然,但,这里有个问题,不得不提下。测试发现:
在
test.php
中,必须引入 proto 文件中声明过的所有结构类型(即使你只用到了里面的一个类型),否则会报错。
比如,注释掉这行 require_once "foo/AddressBook.php";
(事实上,AddressBook 这个类也没有用到), nginx-error 日志输出:
2018/01/19 08:32:10 [error] 6880#6880: *39 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php7.0-fpm.sock:", host: "localhost"
这个就很恶心了,“我只想要个苹果,你却给我一车梨?”。具体原因我还不知道,谁能帮我解释下吗?
总结
php 本身的确是门很简单易用的语言,加上我当年学到的东西还没有忘光,所以重新拾起来还是有故人重逢的感觉。
小试牛刀发现:php7.0 下 protobuf 扩展是可以用的,配置起来也不是很难。
然后,我在调研过程中,还是留了 2 个坑:
php 扩展包的另外一种安装方式
拔一发而动全身:必须引入 proto 文件中声明过的所有结构类型
有收获,也有遗憾,这次 php+protobuf 的填坑之旅就这样告一段落吧。
以上是关于再来一次手牵手:php7.0 + protobuf的主要内容,如果未能解决你的问题,请参考以下文章