为了能更快的响应请求,耗时任务的执行不能阻塞当前脚本执行,而是放在最后执行,比如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 语言协程库