PHP 在 Nginx 下主动断开连接 Connection Close 与 ignore_user_abort 后台运行

来源:互联网 发布:知无涯者评价 编辑:程序博客网 时间:2024/04/25 05:12

这两天弄个PHP调用 SVN 同步 update 多台服务器更新的程序,为了避免 commit 的时候不会被阻塞卡半天得想个办法只请求触发,而不需要等待程序 update 完成返回结果这样耗时太长,所以研究过了下如何让PHP主动断开连接的方法。搞了一下午,发现很多问题,还好最终还是弄出来了,主要是 Nginx 太坑。。


废话不多说,下面上代码:


PHP 在 Nginx 下主动断开连接


/** * 主动断开与客户端浏览器的连接 * 如果是 Nginx 服务器需要输出大于等于 fastcgi_buffer_size 缓存的数据才能即时输出 header 断开连接, 若还是不行可尝试关闭 gzip * 如: fastcgi_buffer_size 64k; 即: 需要 64*1024 字符(可多不可少), * 可使用 str_repeat(' ', 65536); 另外 str_repeat('          ', 6554); 这种方式其实生成速度更慢 * @param null|string $str 当前输出的内容, 若无需输出则设置为空 */public function connectionClose($str = null) {    $str = ob_get_contents() . $str;    // 若实际输出内容长度小于该值将可能导致主动断开失败    header('Content-Length: '. strlen($str));    header('Connection: Close');    ob_start();    echo $str;    ob_flush();    flush();}

补充说明下:

对于 apache 一般没什问题,我一开始在 windows 上用的 xampp 调试的 没发现什么问题,结果到服务器上是 Nginx ,死活不行,崩溃了一下午,后来才反映过来是 Nginx 的 fastcgi_buffer 的问题。

各种情况测试了N多次,应该没什么 BUG 了。。。


另外再说说 ignore_user_abort() 函数的问题

当浏览器关闭后,决定程序是否还会在后台继续执行,(下图的例子中,你在测试时不一定非要设置为永不超时 limit 0 ,设置一两分钟就行了,否则可能重启 HTTP 服务需要很长时间)


PHP ignore_user_abort 后台运行


简单来说,如果你要用户浏览器关闭后还需要程序继续执行,那么你必须加上下面这句代码:

ignore_user_abort(true);

但根据你后面程序(主要是 while 死循环)的情况不同而有些许不同:

一般在程序中你可以监控连接状态进行控制:

$isAborted = connection_aborted();$status = connection_status();if (0 !== $status || $isAborted) {    break;}

但这两个函数要想正常工作得有个前提,就是你的程序必须要有输出内容,且大于当前WebServer 的输出缓存,这样才会起作用。

如果你只是简单的输出一个空格 echo ’ ‘; 可能得循环几千次才会判断到,所以为了更即时的检测到状态你必须每次循环时输出足够多的内容才会触发状态检测。

所以这里也经常会遇到一个问题:当浏览器断开后,即使没有使用 ignore_user_abort(true); 但因为没有任何输出,导致程序仍然会继续执行,死循环会一直跑,如果设置了超时那还好,否则就真死掉了。


下面贴上测试代码(贴个图主要是为了防盗 嘿嘿~)


set_time_limit(0);ignore_user_abort(true);while (1) {    echo str_repeat(' ', 65536);    $isAborted = connection_aborted();    $status = connection_status();    file_put_contents('test.txt', 'time: '. time() .'; abroted:'. $isAborted .'; status: '. $status);    if (0 !== $status || $isAborted) {        break;    }    sleep(2);}

你可以试试注释掉这句
// echo str_repeat(’ ‘, 65536);
另外
set_time_limit(0); 最好也别用 0

0 0