PHP 实现请求结束之后,后台静默执行部分代码

这一篇其实是由上一篇引申出来的。之前说到,勉勉强强实现了 PDF 和 PPT 文件转图片,但是转换需要一定的时间,而我这里是用户发起请求进行转换的,这部分时间不应该让用户一直等着,应该结束请求,把数据返回给用户,然后后台再慢慢执行这些转换操作。

最开始我是有两种想法的,第一种是比较传统的,做一个进度条给用户看,虽然一个文件稍微要几秒钟的等待时间,但是有了进度条之后也勉强能够接受;第二种就是我现在在实现的办法,即结束请求,把数据返回给前端,然后后台再默默地进行转换操作。这里我综合考虑了一下,选择了第二种实现方法。

有了想法的雏形之后,就开始疯狂搜索寻找可行的解决方法了。之前敲代码的时候偶尔也有这种需求,之前的解决方法是利用 crontab,定时执行某些操作,但是放在这里就不合适了,因为这里的任务是实时生成的,且只需要执行一次,而不是定时执行。不过顺着之前的思路,我发现了有一个东西说不定能够满足我的需求——crontab 的好朋友,at。at 和 crontab 的区别基本上就是,crontab 是周期性的,而 at 是一次性的。

本来喜气洋洋的,以为这么快就找到解决方法了,没想到用着用着发现了一个很致命的问题:at 只支持两种生成方式,一是先写一个 bash 脚本,然后添加到临时任务里去,大概像这样:

at -f commandFile now + 10 minutes

第二种方式是,在命令行中输入 at now + 10 minutes,然后再输入具体的指令,形成临时任务。

我寻寻觅觅了好久,仍然找不到能够通过 PHP 生成 at 临时任务的办法,奈何时间有限,只能放弃这个方法,另寻他径了。

我找到的第二个方法是 ThinkPHP5(我使用 PHP 框架)使用官方的插件 think-queue,在稍微了解了一下之后,觉这个东西非常不错,功能好像还蛮强大的,而且挺符合我目前的需求的,但是万万没想到,当我真正跑去用它的时候,出现了那么多的问题。第一,它的安装还挺麻烦的,一开始以为直接用 composer require 一下就可以了,结果还有版本问题,支持 TP5 的之有比较老的一些版本。第二,使用起来还是有点迷的,一方面是既没有找到官方的文档手册,没有找到合适的使用教程,另一方面是但是时间比较着急,没能静下心来研究。不过感觉这东西好像还是不错的,等这段时间期末考完了,打算再去认真研究研究。

上面两种方法皆宣告失败之后,我只能使用最最原始的办法了。话不多说,直接上代码吧:

//清除之前的缓冲内容
ob_end_clean();
//设置响应头连接状态为关闭
header("Connection: close");
//设置HTTP请求状态为200 OK
header("HTTP/1.1 200 OK");
//返回数据格式
header("Content-Type: application/json;charset=utf-8");
//打开输出控制缓冲
ob_start();
//返回数据给前端
echo json_encode($array);
//下面输出http的一些头信息
$size = ob_get_length();
//返回输出缓冲区内容的长度
header("Content-Length: $size");
//输出当前缓冲,并关闭缓冲
ob_end_flush();
//输出PHP缓冲
flush();
//前端接收到数据,请求到此结束,之后的代码都在后台静默执行,前端也无法知道执行情况
if (function_exists("fastcgi_finish_request")) { // yii或yaf默认不会立即输出,加上此句即可(前提是用的fpm)
    fastcgi_finish_request(); // 响应完成, 立即返回到前端,关闭连接
}
//设置客户端断开连接时是否中断脚本的执行
ignore_user_abort(true);
//设置请求时间为不限时
set_time_limit(0);

...这里写需要后台执行的代码

//冲刷(flush)所有响应的数据给客户端,请求完全结束
fastcgi_finish_request();

还是原始的办法好使,没遇到啥困难就把问题给解决了,但是总感觉这样子实现起来太丑了,等有空了还是再去找找更好的办法吧,暂时就这样应付一下好了。

发表评论

邮箱地址不会被公开。 必填项已用*标注