ignore_user_abort 的用法
发现了一个有趣的函数,可以参考参考
总所周知,php的执行流程大概如下
1、客户端(浏览器)发起请求
2、服务器端接受请求
3、niginx开始响应
4、nginx 通过fast-cgi 使用php-fpm处理(swoole及其workerman常驻进程不在此列)
5、程序开始执行
6、程序执行完毕,完成响应周期,返回给客户端(同时间,fpm自动销毁php进程)
由上,我们会发现,php一直都是同步执行,如果当中的某项操作发生了异常或者错误,将会导致整个运行周期的失败,最终执行失败
记住,同步这个词语
在js中,我们或许有 async
在go 中 ,我们有 channel
那如下问题来了,耗时程序如何解决
解决方法
1、使用workerman、swoole等常驻内存框架或者扩展,使用其异步操作完成操作
2、使用发布订阅模式,调用缓存,且使用定时任务维护这个缓存
3、使用ignore_user_abort
官方介绍
ignore_user_abort() 函数设置与客户机断开是否会终止脚本的执行。
本函数返回 user-abort 设置的之前的值(一个布尔值)。
可选。如果设置为 true,则忽略与用户的断开,如果设置为 false,会导致脚本停止运行。
如果未设置该参数,会返回当前的设置
含义则是,当关闭客户端当时候,设置true时,后台进程会继续执行
例子大概如下
ob_end_clean(); # 清空(擦除)缓冲区并关闭输出缓冲
header("Connection: close"); # 关闭连接,会关闭与客户端的连接
header("HTTP/1.1 200 OK"); // 返回哥200的响应
echo "runing";
ignore_user_abort(true); // 后台运行
set_time_limit(0); // 取消脚本运行时间的超时上限
# 以下的脚本将不再执行
echo "我在后台运行呢"
例子解析
我们现在有这样一个案例,我们需要导出一个excel文件,但是数据量异常庞大
大概加上查询时间和数据导出可能需要几分钟
我们需要优化的细节点大概如下
- 快速响应,避免等待
- 防止数据量大崩溃
- 等等
解决过程
设置
ini_set('memory_limit', '2048M'); # 设置内存
ini_set('max_execution_time', 0); # 持续到php进程结束
set_time_limit(0); # 设置运行时间
# 耗时代码块
代码
1: 执行异步程序来导出
# 执行耗时程序
$filename = self::FILE_NAME_KEY . '_' .date('mdHis') . '.xlsx';
Cache::put(self::FILE_NAME_KEY, $filename, Carbon::now()->addMinute(2));
ob_end_clean();
header("Connection: close");
header("HTTP/1.1 200 OK");
ignore_user_abort(true); // 后台运行
set_time_limit(0); // 取消脚本运行时间的超时上限
# 耗时程序
$thirdDistributeStore->exportList($platform, $tagId, $status, $shopName, $productName, $page, $pageSize, $filename);
2: 获取导出文件信息和下载地址
$filename = Cache::get(self::FILE_NAME_KEY);
if(!$filename) {
return $this->response(-1, 'error', []);
}
$httpType = $httpType = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
$url = $httpType.$_SERVER['HTTP_HOST'] . '/export-dowload/' . $filename;
return $this->response(0, 'success', ['dowload_url' => $url]);
3: 进入获取到到地址去下载,因为程序在运行中,如果文件不存在可以刷新重试
$pathToFile = storage_path("exports/$file");
if(!file_exists($pathToFile)) {
echo '文件可能还在写入中,请刷新重试';
die;
}
return response()->download($pathToFile);
本文由 邓尘锋 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Mar 22, 2021 at 04:50 pm