目前已在公司项目中完美使用,应用场景仅适合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子进程,或使用其它解决方案。