PHP中如何实现异步非阻塞,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
在PHP5.3.3版本之后,不管是Nginx还是Apache服务器,只要运行在FastCGI模式下,均可使用该方法,官方解释的作用是冲刷(flush)所有响应的数据给客户端。
boolean fastcgi_finish_request ( void )
此函数冲刷(flush)所有响应的数据给客户端并结束请求。 这使得客户端结束连接后,需要大量时间运行的任务能够继续运行。
用法:可以在读写大文件、循环更新数据库等不影响结果的操作之前,执行该函数,把结果返回给客户端,php会继续执行下面的逻辑而不影响客户端的响应时间。
fsockopen()方法可以打开一个网络连接或Unxi套接字连接,stream_set_blocking()方法可以为资源流设置非阻塞或者阻塞模式,
使用fsockopen()打开一个网络连接或者一个Unix套接字连接,再用stream_set_blocking()设置资源成非阻塞模式请求,则该资源请求会是非阻塞的:
bool stream_set_blocking ( resource $stream , int $mode )
注:$mode=0则是非阻塞的,1则是阻塞的模式。
用法:
<?php $fp = fsockopen('www.oschina.net', 80, $errno, $errstr, 30); if( !$fp ) { die('error fsockopen'); } // 转换到非阻塞模式 stream_set_blocking($fp, 0); $http = "GET /Save.php / HTTP/1.1\r\n"; $http .= "Host: www.oschina.net\r\n"; $http .= "Connection: Close\r\n\r\n"; fwrite($fp, $http); while (!feof($fp)) { echo fgets($fp, 128); } fclose($fp);
cURL除了我们通常使用的curl_init来初始化和发送post和get请求之外,还可以使用curl_multi_init()方法来实现异步请求,其原理是使用系统的select这个多路I/O复用机制来异步发送请求。
通常的用法:
<?php // 创建一对CURL资源 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "www.oschina.net"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); $output = curl_exec($ch); echo $output;
异步用法:
<?php $time = time(); // 创建一对CURL资源 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "www.oschina.net"); curl_setopt($ch, CURLOPT_HEADER, TRUE); // 增加2个句柄 $mh = curl_multi_init(); curl_multi_add_handle($mh, $ch); // 执行批处理句柄 $running= null; do { usleep(10000); // 延迟10000微秒 curl_multi_exec($mh, $running); } while ( $running>0 ); // 关闭全部句柄 curl_multi_remove_handle($mh, $ch); curl_multi_close($mh); echo "\n total time: ".(time()-$time)."\r";
4、使用Gearman/Swoole等PHP异步扩展或框架
5、使用缓存和队列
通常复杂的处理逻辑,我们可以将参数推入队列,如redis或kafka的队列服务,开启一个消费进程去处理队列事务。
如redis服务的list结构,在之前篇章有过说明。
6、使用pcntl_fork()
在PHP4.1.0版本之后都支持了该函数,使用前需要安装支持pcntl扩展并添加支持。
官方文档:在当前进程当前位置产生分支(子进程),这个子进程仅PID(进程号) 和PPID(父进程号)与其父进程不同
说明:int pcntl_fork ( void )
成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0;
失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。
用法:
<?php $pid = pcntl_fork(); // 父进程和子进程都会执行下面代码 if( $pid== -1 ) { // 错误处理: 创建子进程失败时返回-1 die('could not fork'); } elseif ( $pid ) { //父进程会得到子进程号,所以这里是父进程执行的逻辑 pcntl_wait($status); // 等待子进程中断,防止子进程成为僵尸进程 echo '父进程:'.$status; } else { // 子进程得到的$pid为0, 所以这里是子进程执行的逻辑 echo '子进程'; } exit();
并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO、协程。阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。现在网络的日益发展,对响应时间和处理并发的要求越来越高,所以异步非阻塞的需求也越来越多,优点也显而易见:
1、非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程,能及时返回响应,减少响应时间;
2、高并发,同步阻塞IO模型的并发能力依赖于进程/线程数量,响应时间的下降可以带来更多的并发能力。
当然,异步非阻塞又存在缺点:
1、启动大量进程会带来额外的进程调度消耗。非阻塞模式下,如果大量线程已经返回响应而仍然在执行计算操作,会使CPU利用率不可控的增高;
2、这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的;
3、过多的异步和多线程模型会造成编码困难或线程混乱,如出现大量僵尸进程等。
1、一般情况下,我们不赞成用异步回调的方式去做功能开发,传统的PHP同步方式实现功能和逻辑是最简单的,也是最佳的方案。像node.js这样到处callback,只是牺牲可维护性和开发效率;
2、有些时候很适合用异步,比如FTP、聊天服务器,smtp,代理服务器等等此类以通信和读写磁盘为主,功能和业务逻辑其次的服务器程序;
3、异步非阻塞和多线程模型推荐:
(1)swoole框架:fpm里,通过swoole_client把url发送到swoole的server,swoole_server天然支持并行请求,把汇总的结果返回到fpm;
这也是当下PHP最火的异步多线程框架,可以了解一下韩天峰的文章:http://rango.swoole.com/
(2)recoil框架:https://github.com/recoilphp/recoil
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。