为了能更快的响应请求,耗时任务的执行不能阻塞当前脚本执行,而是放在最后执行,比如fastcgi_finsh_request。现在又多了一个方法,PHP也可以进行异步IO处理了。
PHP扩展eio是基于C语言的libeio库中开发的异步IO处理,可用于异步文件读写,自定义异步任务执行。Nodejs使用的libuv库封装了libeio和libev(libev也有对应的PHP扩展:ev),前者提供异步IO,后者提供高性能的事件驱动,进行高性能的请求的处理。另外,PHP异步多线程框架Swoole也提供了异步IO处理的能力。
在CentOS下编译安装PHP eio扩展:
1 2 3 4 5 6 7 | 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添加扩展
1 2 | [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可以自定义异步任务调用。
以下以写文件进行比较,示例一是常规的文件写操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $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的异步写操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | $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扩展的异步写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $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 语言协程库