PHP的非阻塞或并行请求实现方式
我们都知道,php是串行请求的,我们在碰到以下几个场景的时候,php的效率会变的比较低下:
1:一个请求,在输出结果前,有比较长时间的耗时操作(但这个操作不影响输出结果),这时候,客户端会等待比较长的时间。
2:如果我要同时获得多个远程接口的结果,耗时是所有接口响应耗时之和
那么,有没有办法来提升上述场景的效率呢?
答案是肯定的:
1:针对第一个场景,若你使用的是FastCGI模式 ,使用fastcgi_finish_request()能把结果输出到客户端,但PHP进程继续在跑
fastcgi_finish_request();
echo"xtgxiso"//这儿结束之后的执行
备注:此方式不能算是非阻塞,只是把结果尽快输出到客户端,但本身这个php进程还是被阻塞的占用着。
2:使用curl_multi_init的方法,进行并行url请求
$time=time();
// 创建3个cURL资源
$ch1=curl_init();
$ch2=curl_init();
$ch3=curl_init();
// 设置URL和相应的选项
curl_setopt($ch1,CURLOPT_URL,
"http://test.xtgxiso.cn/sleep1.php");
curl_setopt($ch1,CURLOPT_HEADER,0);
curl_setopt($ch2,CURLOPT_URL,
"http://test.xtgxiso.cn/sleep2.php");
curl_setopt($ch2,CURLOPT_HEADER,0);
curl_setopt($ch3,CURLOPT_URL,
"http://test.xtgxiso.cn/sleep3.php");
curl_setopt($ch3,CURLOPT_HEADER,0);
// 创建批处理cURL句柄
$mh=curl_multi_init();
// 增加3个句柄
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
curl_multi_add_handle($mh,$ch3);
$running=null;
// 执行批处理句柄
do{usleep(10000);curl_multi_exec($mh,$running);
}
while($running>0);
// 关闭全部句柄
curl_multi_remove_handle($mh,$ch1);
curl_multi_remove_handle($mh,$ch2);
curl_multi_remove_handle($mh,$ch3);
curl_multi_close($mh);
echo"\n total time : ".(time()-$time)."\n";
3:使用stream_set_blocking + select方法,进行并行url请求
// url数组,每个url发送一个请求
$urls=array("sleep1.php","sleep2.php","sleep3.php"
);
// 保存socket的数组
$sockets=array();
$time=time();
// 批量创建链接并发送数据
foreach($urlsas$url){$socket=stream_socket_client("tcp://test.xtgxiso.cn:80",$errno,$errstr,3);// 设置成非阻塞stream_set_blocking($socket,0);fwrite($socket,"GET /{$url} HTTP/1.0\r\nHost: test.xtgxiso.cn\r\nAccept: */*\r\n\r\n");// 记录数组$sockets[(int)$socket]=$socket;
}
// 批量等待数据返回
while(count($sockets)>0){$read=$sockets;$write=$e=array();// 等待数据可读if(stream_select($read,$write,$e,10)){// 循环读数据foreach($readas$socket){// 这里是服务端返回的数据,需要的话可以循环读echo fread($socket,8192)."\n";// 数据读取完毕关闭链接,并删除链接fclose($socket);unset($sockets[(int)$socket]);}}
}
echo time()-$time;echo"\n";
上面两种方式请求多个url的时候,时间由原来的所有请求响应时间之和变为只是最长的那个请求的响应时间(如请求1:10ms,请求2:15ms,请求3:20ms,串行处理的时间将是:45ms,而并行处理的时间只有:25ms),从而提高效率和并发,我们可以封装个get请求的方法,post方法和get类似!
functionhttp_get_url($arr=''){if(is_array($arr)&&$arr){$sockets=array();foreach($arras$url){$url_info=parse_url($url);if(!@$url_info["port"]){$url_info["port"]=80;}$socket=stream_socket_client("tcp://".$url_info["host"].":".$url_info["port"],$errno,$errstr,3);if($socket){stream_set_blocking($socket,0);$str="GET ".$url_info["path"]."?".$url_info["query"]." HTTP/1.0\r\nHost: ".$url_info["host"]."\r\nAccept: */*\r\n\r\n";echo $str;fwrite($socket,$str);$sockets[(int)$socket]=$socket;}}while(count($sockets)>0){$read=$sockets;$write=$e=array();if(stream_select($read,$write,$e,3)){foreach($readas$socket){$result[(int)$socket].=fread($socket,8192);unset($sockets[(int)$socket]);}}}foreach($resultas$k=>$v){$result[$k]=trim(strstr($v,"\r\n\r\n"),"\r\n");}return$result;}elseif($arr){$curl=curl_init();curl_setopt($curl,CURLOPT_URL,$arr);curl_setopt($curl,CURLOPT_HEADER,0);curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);curl_setopt($curl,CURLOPT_TIMEOUT_MS,3000);curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);curl_setopt($curl,CURLOPT_MAXREDIRS,3);$result=curl_exec($curl);curl_close($curl);return$result;}else{returnfalse;}}
这个方法可以get一个或多个url,尤其在多个的时候,可以提高效率!
备注:
1: curl_multi和stream_select 都是调用 系统的select进行多路i/o复用
2: 并行请求的场景,用swoole也更合适,fpm里,通过swoole_client,把url发送到swoole的server, swoole_server天然支持并行请求,把汇总的结果返回到fpm。
--------------伟大的分割线----------------
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。
也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投搞。
投稿请联系:
本文由张素杰独家授权 php饭米粒发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)