温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PHP中如何实现异步非阻塞

发布时间:2021-06-21 17:38:28 来源:亿速云 阅读:386 作者:Leah 栏目:大数据

PHP中如何实现异步非阻塞,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

一、各种实现方法

1、FastCGI的非阻塞方法:fastcgi_finish_request()

在PHP5.3.3版本之后,不管是Nginx还是Apache服务器只要运行在FastCGI模式下,均可使用该方法,官方解释的作用是冲刷(flush)所有响应的数据给客户端。
boolean fastcgi_finish_request ( void )
此函数冲刷(flush)所有响应的数据给客户端并结束请求。 这使得客户端结束连接后,需要大量时间运行的任务能够继续运行。
用法:可以在读写大文件、循环更新数据库等不影响结果的操作之前,执行该函数,把结果返回给客户端,php会继续执行下面的逻辑而不影响客户端的响应时间。

2、fsockopen()+stream_set_blocking()方法:

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);

3、使用cURL执行异步请求:

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

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

php
AI