标签归档:swoole

PHP异步IO

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

查看是否安装成功
eio0
注意: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_customeio_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";
});

对比情况
eio1
可以看出,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 语言协程库