原生PHP代码实现耗时任务后台异步伪并发执行

目前已在公司项目中完美使用,应用场景仅适合NGINX+PHP-FPM。APACHE FCGI模式未测试。伪并发执行指NGINX给FPM子进程分配任务时,可以多个进程实现同时工作,并非处理高并发请求。

// 设置客户端断开连接时不中断脚本的执行
ignore_user_abort(true);
 
// 以下代码开始告诉NGINX响应已经成功得到响应内容可以关闭请求了。
# 擦除缓冲区的内容并关闭,然后在启动新的ob缓冲
ob_end_clean(); ob_start();
# 输出响应数据,这里模拟输出json。320:中文不编码+不转移斜杠[JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES] = 320
echo json_encode(['status' => true, 'message' => '任务开始执行', 'date' => null], 320);
$size = ob_get_length();
# 响应内容长度
header("Content-Length: $size");
# 告诉NGINX可以关闭http连接了
header("Connection: close");
# 此次请求已收到并正常处理
header("HTTP/1.1 200 OK");
 
# 刷新输出缓冲区内容并关闭ob缓冲
ob_end_flush(); 
# 如果缓冲区还有内容,再次刷新输出ob缓冲块的内容
ob_get_length() && ob_flush();
# 再次刷新输出,冲刷Web Server的缓冲区。例如用于防范NGINX GZIP或 APACHE mod_gzip自己的缓冲区
flush();
# 冲刷所有响应的数据给客户端。
function_exists("fastcgi_finish_request") && fastcgi_finish_request();
session_write_close(); # 关闭session写入,取消对session进行IO的锁
 
// 设置最大执行时间,这里为15分钟
ini_set('max_execution_time', 15 * 60);
set_time_limit(15 * 60);
// 额外设置
error_reporting(0); # 屏蔽所有报告异常
ini_set('memory_limit', '512M'); # 临时设置内存,若默认运行内存128M够用,无需再次设置
date_default_timezone_set('Asia/Shanghai'); # 时区
 
// code ...
// error_log('任务执行完成'); UPDATE TABLE ...
 
exit;

为何要冲刷那么多次缓冲区?

PHP的数据写入顺序: ob_start(),将内部缓冲区(buffer)打开。当PHP遇到echo,printf等输出语句时,PHP就会将要输出的数据放入缓冲区(buffer)中,等待输出。而只有当缓冲区满了或者PHP运行完毕,才将数据输出去。输出字节离开PHP缓冲区进去Apache缓冲区或者Nginx缓冲区(fast-cgi),之后进入浏览器缓冲区。

注意:此代码不适合高并发请求大耗时任务巨长的工作

因FPM子进程工作满载中,NGINX无法分配任务,以至于任务不能及时处理,造成NGINX报出502错误,导致任务处理失败。这种场景可选择开启更多的NGINX WORKER进程和更多的FPM子进程,或使用其它解决方案。

原文地址:https://www.cnblogs.com/qiutianjia/p/11573377.html