php提前响应请求继续执行代码(伪异步)
- 2019 年 12 月 19 日
- 笔记
在很多业务需求中,我们都可能需要先让php给浏览器输出,然后在后台慢慢处理其他不用输出耗时的业务.
那么,php该怎么实现这个功能呢?
ignore_user_abort(true);
首先,我们先来了解下ignore_user_abort(true);这个函数
这个函数可以忽略客户机的断开,继续执行php代码
那到底这个用来干啥的呢?例如:
//当用户A用浏览器请求下单逻辑 //由于后台逻辑非常多,需要处理20秒 //用户A等了10秒等不下去,关闭了网页 //默认情况下,用户关闭了网页,php进程则会直接终止,相当于执行了一半逻辑之后,停止了 //用户后面发现,自己已经有了这个订单数据,却没有订单详情(执行一半没来得及插入)
这个时候,ignore_user_abort就有用了,当忽略客户机断开后,php会一直执行,直到异常终止或已完成操作
set_time_limit(0);
在上面讲到,如果启用ignore_user_abort 则会让php一直执行,直到异常终止,而在php常规web模式下,默认有个执行超时时间(30秒),当执行到30秒时,会直接终止该php进程,可使用set_time_limit(0),设置为用不超时,这样的话,客户端就算断开,就算超过30秒,php进程也会一直执行下去,直到执行完成
实时输出
在我之前的一篇讲buffer缓冲区的文章中,有讲到过浏览器实时输出,刷新缓冲区可以让php+web服务器的输出变成实时输出,不再需要等待脚本结束才显示内容.然而,apache和nginx的实现方式也有所不同
<?php //apache方法,需要关闭apache缓冲区 for($i=0;$i<1000;$i++){ echo $i; ob_flush();//刷新PHP自身缓冲区 flush();//刷新(特指apache)web服务器的缓冲区,输出数据 sleep(1); } //nginx缓冲区 ob_end_clean(); ob_implicit_flush(); header('X-Accel-Buffering: no'); // 关键是加了这一行。 for($i=0;$i<1000;$i++){ echo $i; sleep(1); }
用以上方法,就可以使php的echo,实时输出到浏览器中
伪结束响应
在认识到上面3种概念之后,我们就要开始实现这个功能了 伪结束响应原理是:
先让php提前输出"已结束响应"代码(其实还没有结束,还可以继续echo输出)
然后让用户自行关闭窗口,通过set_time_limit和ignore_user_abort函数实现php代码还在后台运行,如以下例子:
<?php //apache服务器 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉这里处理了一些事情 echo "完成请求,3秒自动关闭页面(一段js自动关闭页面)"; ob_flush();//刷新PHP自身缓冲区 flush();//刷新(特指apache)web服务器的缓冲区,输出数据 //这里还在巴拉巴拉处理事情 $i=0; while(1){ //注意,死循环非常危险,会造成该web进程一直在处理,不会退出,永久占用一个进程,而且管理该进程非常麻烦,建议加个判断啥的 file_put_contents('test.txt',$i); $i++; sleep(1); } //nginx服务器 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉这里处理了一些事情 ob_end_clean(); ob_implicit_flush(); header('X-Accel-Buffering: no'); // 关键是加了这一行。 echo "完成请求,3秒自动关闭页面(一段js自动关闭页面)"; //这里还在巴拉巴拉处理事情 $i=0; while($i<100){ //注意,死循环非常危险,会造成该web进程一直在处理,不会退出,永久占用一个进程,而且管理该进程非常麻烦,建议加个判断啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
提前结束响应
在php-fpm中,有个函数fastcgi_finish_request可使得web服务器提前中断http响应:
<?php //php-fpm模式下 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉这里处理了一些事情 echo "完成请求,3秒自动关闭页面(一段js自动关闭页面)"; fastcgi_finish_request();//真正的结束响应,后面的echo将不起作用 //这里还在巴拉巴拉处理事情 $i=0; while($i<100){ //注意,死循环非常危险,会造成该web进程一直在处理,不会退出,永久占用一个进程,而且管理该进程非常麻烦,建议加个判断啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
在非fpm模式下,该怎么提前中断呢?
<?php //非php-fpm 一般是apache set_time_limit(0); ignore_user_abort(true); ob_end_flush(); ob_start(); //巴拉巴拉这里处理了一些事情 echo '完成请求,3秒自动关闭页面(一段js自动关闭页面)'; header("Content-Type: text/html;charset=utf-8"); header("Connection: close");//告诉浏览器不需要保持长连接 header('Content-Length: '. ob_get_length());//告诉浏览器本次响应的数据大小只有上面的echo那么多 ob_flush(); flush(); //header不会经过buffer,直接输出到浏览器,浏览器接收到之后,直接主动关闭连接 //这里还在巴拉巴拉处理事情 $i=0; while($i<100){ //注意,死循环非常危险,会造成该web进程一直在处理,不会退出,永久占用一个进程,而且管理该进程非常麻烦,建议加个判断啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn
- 上一篇: 关于"绝对路径"和"相对路径"
- 下一篇: swoole/easyswoole 新手入门教程