你不了解的php函数之 ignore_user_abort

in PHP with 0 comment

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


Comments are closed.