为了能更快的响应请求,耗时任务的执行不能阻塞当前脚本执行,而是放在最后执行,比如fastcgi_finsh_request。现在又多了一个方法,PHP也可以进行异步IO处理了。
PHP扩展eio是基于C语言的libeio库中开发的异步IO处理,可用于异步文件读写,自定义异步任务执行。Nodejs使用的libuv库封装了libeio和libev(libev也有对应的PHP扩展:ev),前者提供异步IO,后者提供高性能的事件驱动,进行高性能的请求的处理。另外,PHP异步多线程框架Swoole也提供了异步IO处理的能力。
在CentOS下编译安装PHP eio扩展:
wget https://pecl.php.net/get/eio-1.2.5.tgz tar -zxvf eio-1.2.5.tzg cd eio-1.2.5 phpize ./configure sudo make sudo make install
更改php.ini添加扩展
[eio] extension=eio.so
查看是否安装成功

注意:eio扩展必须在socket扩展之后加载,否则会提示’ undefined symbol: php_sockets_le_socket in Unknown on line 0’。
PHP eio支持libeio的所有操作,当调用eio_poll/eio_event_loop时,触发IO处理,处理完成进行回调;未主动调用eio_poll/eio_event_loop则不阻塞当前程序执行,等待脚本执行结束后在提交异步处理。使用eio_custom和eio_nop可以自定义异步任务调用。
以下以写文件进行比较,示例一是常规的文件写操作:
$strBasePath = dirname(__FILE__);
$strFileName = $strBasePath.'/file.log';
$strContent = 'something to log ...';
$nStart = microtime(true);
$rFile = fopen($strFileName ,'a+');
for($i=0;$i<1000;$i++){
$nLength = fwrite($rFile, $strContent);
}
fclose($rFile);
echo "done\r\n";
register_shutdown_function(function(){
global $nStart;
echo microtime(true) - $nStart;
echo "\r\n";
});
示例二是PHP eio的异步写操作:
$strBasePath = dirname(__FILE__);
$strFileName = $strBasePath.'/file.log';
$strContent = 'something to log ...';
$nLenth = strlen($strContent);
function eioOpenCallBack($p_mxData ,$p_mxResult){
//echo "Open.\r\n";
if ($p_mxResult > 0) {
eio_fstat($p_mxResult, EIO_PRI_DEFAULT, "eioStatCallBack", $p_mxResult);
}
}
function eioStatCallBack($p_mxData ,$p_mxResult){
global $strContent ,$nLenth;
//echo "Stat.\r\n";
if ($p_mxResult > 0) {
eio_write($p_mxData, $strContent, $nLenth, $p_mxResult['size'], EIO_PRI_DEFAULT, "eioWriteCallBack", $p_mxData);
}
}
function eioWriteCallBack($p_mxData ,$p_mxResult){
//echo "Write.\r\n";
if ($p_mxResult > 0) {
eio_close($p_mxData, EIO_PRI_DEFAULT, "eioCloseCallBack", $p_mxData);
}
}
function eioCloseCallBack($p_mxData ,$p_mxResult){
//echo "Close.\r\n";
if($p_mxResult == 0){
//echo "End\r\n";
}
}
$nStart = microtime(true);
for($i=0;$i<1000;$i++){
eio_open($strFileName, EIO_O_CREAT | EIO_O_RDWR, EIO_S_IRUSR | EIO_S_IWUSR,EIO_PRI_DEFAULT, "eioOpenCallBack", $strFileName);
//echo "Begin\r\n";
//eio_event_loop();
}
echo "done\r\n";
register_shutdown_function(function(){
global $nStart;
echo microtime(true) - $nStart;
echo "\r\n";
});
示例三是swoole扩展的异步写操作
$strBasePath = dirname(__FILE__);
$strFileName = $strBasePath.'/file.log';
$strContent = 'something to log ...';
$nStart = microtime(true);
for($i=0;$i<1000;$i++){
swoole_async_write($strFileName ,$strContent);
}
echo "done\r\n";
register_shutdown_function(function(){
global $nStart;
echo microtime(true) - $nStart;
echo "\r\n";
});
对比情况

可以看出,eio和swoole提交异步IO处理后,处理非常快,并未阻塞当前进程运行;eio更像是只花了循环的时间。swoole只能运行于CLI模式,使用了Linux Native AIO,处理非常快,写法也比较简单。eio可以运行于CGI和CLI模式,提交异步处理后会创建至多4个线程(eio_nthreads)进行处理,处理完成后仍然会返回主线程,所以PHP脚本执行结束后,主线程仍然会在在那里等待。eio的异步处理通过回调进行保证,写法上更加复杂。这就需要对异步回调进行包装,提供类似同步的代码,参见协程。
参考链接:
PHP异步执行长时间任务
PHP的生命周期
libeio源码学习
PHP新增eio扩展,可以写类似node.js一样的异步IO了
深入浅出Node.js(五):初探Node.js的异步I/O实现
异步AIO的研究
关于C10K、异步回调、协程、同步阻塞
协程
linux AIO (异步IO) 那点事儿
什么程序设计语言机制是处理异步 IO 最恰当的抽象?
nodejs异步IO的实现
向facebook学习,通过协程实现mysql查询的异步化
Python 中的进程、线程、协程、同步、异步、回调
一个“蝇量级” C 语言协程库