Nginx/PHP FPM优美的停止(SIGQUIT):不那么优美


Nginx / PHP FPM graceful stop (SIGQUIT): not so graceful

运行nginx 1.9.*/PHP 7.0.*(但在5.6.*中也有完全相同的行为)

在维护期间,试图优雅地停止PHP-FPM/nginx组合以关闭节点。为此,我将SIGQUIT发送到php-fpm,应该提供一个优雅的关闭。

为了测试这一点,我制作了一个愚蠢的脚本

<?php sleep(5); echo 'done';

使用以下卷曲进行局部测试

curl -I x.x.x.x:8080

通常产生输出:

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Apr 2016 04:48:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close

所需:在任何飞行中请求的中间,当请求正常关闭时,当前请求应该完成,但任何其他请求都应该失败。

不幸的是,当我试图通过发送SIGQUIT来触发这种行为时(http://manpages.ubuntu.com/manpages/precise/man8/php5-fpm.8.html)到PHP-FPM主进程:

kill -s SIGQUIT $FPMPID

连接立即中断,导致ngnix 502

HTTP/1.1 502 Bad Gateway
Server: nginx
Date: Tue, 12 Apr 2016 04:48:07 GMT
Content-Type: text/html
Content-Length: 166
Connection: close

有什么建议吗?我很想让这个系统尽可能无缝。谢谢

在与同样的情况斗争了一段时间之后,我相信我已经找到了神奇的配置设置,可以让子进程在死亡之前完成处理请求。

http://php.net/manual/en/install.fpm.configuration.php#process-控制超时

process_control_timeout

子进程等待对来自主的信号作出反应的时间限制

基本上,通过将其设置为类似10s的值,子进程将等待那么长时间,同时在退出之前处理现有请求。

不幸的是,php-fpm主进程似乎会立即退出,因此,受此处代码的启发,我编写了一个包装脚本:

#!/bin/bash
PHP_FPM_PID='/php-fpm.pid'
wait_for_pid () {
    try=0
    while test $try -lt 35 ; do
        if [ ! -f "$1" ] ; then
            try=''
            break
        fi
        echo -n .
        try=`expr $try + 1`
        sleep 1
    done
}
function clean_up {
    echo "Killing $(cat $PHP_FPM_PID)"
    kill -QUIT `cat $PHP_FPM_PID`
    wait_for_pid $PHP_FPM_PID
    echo "Done!"
    exit 0
}
trap clean_up EXIT
nohup php-fpm --daemonize --pid $PHP_FPM_PID 2>&1 &
while true; do sleep 1; done
# ^ do nothing forever

等待35秒或直到pid文件被删除(可能是由一个子进程删除的?我仍然不清楚是如何删除的)。

无论如何,这个包装器脚本与我们使用Kubernetes运行的php-fpm docker容器的CMD一样工作得很好。