(译)提升PHP程序性能
这是一篇介绍如何提升PHP程序性能的文章,大多数应用程序并没有真正地运用PHP的全部功能,只是简单认为只要激活了APC(APC的全称为Alternative PHP Cache,是一款开源的php插件,用来对php的代码或者用户数据进行缓存)缓存就已经极致地提升了性能。如果你对此仍有兴趣,请继续读下去。
在一个大的Symfony应用程序里使用这个方法,能够使每秒处理请求量从130提升到的2000
体系结构
先来回顾一下“历史”。
我们一贯的做法是使用一些众所周知的web服务器(apache、nginx等)来配合PHP处理HTTP协议并将动态请求重定向到PHP,与Apache的mod_rewrite这样的重写引擎一起变得非常强大。
配置web服务器运行PHP的几种方法:
- mod_php(只适用apache)
- f(ast)cgi
- php-fpm
考虑到安全因素,通常配置使用FCGI和SuExec,保证每个解释器进程都在特定的站点用户下运行。这种分离使得每个客户机无需虚拟机就可以进行大规模的托管。在本地开发机或单一的应用程序服务器使用mod_php或php-fpm是很常见的。
这些配置在整个行业内都很常见。但是不得不说一下它的缺点,即使开启了opcode cache
(操作码缓存),每当有新的请求时,仍需要声明类、实例化类、读取缓存。正如你想象的那样,这是非常耗时的,远不是高性能配置。
在盒子外思考
那么,我们为什么要这么做呢?为什么在每次请求之后,都会销毁PHP为应用程序所建立的内存,再次请求后再重新建立内存呢?嗯,这是因为PHP起初的设计理念只是一个模板引擎、一套工具,并不是基于服务端的语言。PHP本身并不是真正的异步设计,几乎所有的功能都是“阻塞”的。多年来PHP发生巨大的变化,现在,我们使用PHP能够写出强大的模板引擎,得益于composer丰富的扩展库,PHP已经有了一个健全的“生态系统”。总之,其它语言有的我们要有,其它语言没有的我们也要有,PHP是全世界最好的语言。(原作太长,一句话概括下)。我们甚至有PHP的异步Web服务器库。
等等,什么?
等等,我们有PHP异步库?是的,我们有。ReactPHP对“我”来说是一个最有前途的PHP库,它带来了事件驱动的强大概念和非阻塞I/O。运用这项技术,我们能够在PHP中编写自己的HTTP堆栈,并在每次请求结束时将其控制在内存中而不是销毁它。
这很易于理解,当我们不需要实例化所有的对象时,读缓存,我们将获得更多的性能。因为大部分时间引导应用程序是响应时间的一个重要因素。当移除这些因素,它的机制就类似java、nodejs和Co那样。
怎么回事?
So easy!只需使用ReactPHP即可。
使用composer安装1
composer require 'react/react=*'
然后创建文件server.php
并调用它:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?php
require 'vendor/autoload.php';
$i = 0;
$app = function ($request, $response) use ($i) {
$response->writeHead(200, array('Content-Type' => 'text/plain'));
$response->end("Hello World $i\n");
$i++;
};
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket, $loop);
$http->on('request', $app);
echo "Server running at http://127.0.0.1:1337\n";
$socket->listen(1337);
$loop->run();
现在你可以用server.php
开始你的第一个PHP Web服务器。打开浏览器访问http://127.0.0.1:1337
,将会看到Hello World
的消息。应用程序是主函数,它是每个请求的入口点。
基准时间
现在你可能会想,“嗯,PHP只使用一个CPU线程,它没有利用我的多核服务器的整体能力”。确实如此,但是,
现在我们需要一个多核服务器来产生一些进程,(大概意思是他写了一个东西),地址是:https://github.com/php-pm/php-pm”
看如下所示设置:
安装程序:
- Intel(R) Xeon(R) CPU L5630, 6 Cores
- 8GB RAM
- PHP 5.4.4 with APC
- Debian 7.1
- nginx/1.2.1
PHP-FPM和React服务器都有6个子进程
使用Apache HTTP服务器基准测试工具进行测试(ab)。
我们现在要测试的是一个基于symfony2.4+的Web应用程序,地址点这里,这是一个包含模板、缓存、数据库、事件监听等多项服务的CMS系统。
缓存配置只缓存基本内容,视图的结果不缓存,所以我们没有缓存更多的数据。
启动React服务器的命令是:1
php ./bin/ppm start /path/to/symfony/ --bridge=symfony -vvv
hhvm:1
hhvm ./bin/ppm start /path/to/symfony/ --bridge=symfony -vvv
结果如下:
每秒请求数
内存使用
说明
- fpm - nginx服务后面的常规php-fpm服务
- react - React与内置负载均衡服务器(见上上上图)
- react+nginx - React与nginx负载均衡服务器。React只负责产生服务器子进程,nginx用于连接子进程。
- hhvm - 作为开启hhvm的React服务器
- hhvm+nginx - 作为开启hhvm的React+nginx服务器
hhvm使用stream_select
事件循环,PHP使用libevent
,所以,我们不使用hhvm内置的Web服务器。
正如我们所看到的,React+nginx负载均衡服务器比传统PHP+APC快15倍之多,每秒也可以应付2000的请求量。如果是这样的结果:
1 | 2.000 requests / second |
内存使用率几乎一样,内存连续增加可能是因为CMS内存泄露引起的。这样的环境还没有优化,请参见下面获取更多有关此方法问题的信息。
(大概意思是推荐你试一试)
nginx负载均衡
react+nginx和hhvm+nginx配置如下:
我们在这里所做的只是代理请求而并不指向本地文件。
1 | upstream backend { |
采用这种方法的问题
使用这种方法一定要注意的几个问题。
1.我们必须更好地处理内存以避免泄漏。PHP的较新版本做得相当不错,但是在处理请求之后破坏整个应用程序的内存的旧方法并不重视这个话题。(翻译的不好)
2.每次文件更改时,我们必须重新启动服务器。因为在PHP中不可能重新定义“符号”(类、方法)。我计划通过PHP进程管理器来解决这个问题,用它来启动子进程。
3.当抛出异常时,必须重新启动服务器。我计划通过PHP进程管理器找到一个解决方案。
4.尽管ReactPHP具有异步能力,但是并没有被主流框架所支持。这意味着,如果你要提升web程序的性能,必须使用异步方式重写你的应用程序。这是可以实现的,总的来说更快,但这是一项极其复杂的工程。因此,可以借鉴Node.js如何使用异步。我的意见是值得一试。
5.如果你用ReactPHP重构框架,必须确保该框架能够根据请求分离其内部数据。例如,Symfony框架。