【问题标题】:How to gracefully restart django running fcgi behind nginx?如何优雅地重启 django 在 nginx 后面运行 fcgi?
【发布时间】:2010-09-26 14:04:28
【问题描述】:

我正在使用 fcgi 连接的 nginx 后面运行一个 django 实例(通过使用 manage.py runfcgi 命令)。由于代码已加载到内存中,我无法在不杀死并重新启动 django fcgi 进程的情况下重新加载新代码,从而中断实时网站。重启本身非常快。但是通过首先杀死 fcgi 进程,一些用户的操作会被打断,这是不好的。 我想知道如何重新加载新代码而不会造成任何中断。建议将不胜感激!

【问题讨论】:

    标签: django nginx deployment fastcgi


    【解决方案1】:

    我会在新端口上启动一个新的 fcgi 进程,更改 nginx 配置以使用新端口,让 nginx 重新加载配置(这本身很优雅),然后最终停止旧进程(您可以使用 netstat 查找当与旧端口的最后一个连接关闭时退出)。

    或者,您可以更改 fcgi 实现以派生一个新进程,关闭子进程中除 fcgi 服务器套接字之外的所有套接字,关闭父进程中的 fcgi 服务器套接字,在子进程中执行一个新的 django 进程(使其使用fcgi 服务器套接字),并在所有 fcgi 连接关闭后终止父进程。 IOW,为 runfcgi 实现优雅重启。

    【讨论】:

    • 如果你把一个新的fcgi放在一个新的端口上,nginx不会把已经登录的用户转发到新进程吗?这将与冷重启 fcgi 进程相同
    • 确实会将所有用户转发到新进程;这没有害处。冷重启的问题是正在运行的进程被杀死,所以 in-progress HTTP 请求失败。这就是 OP 担心的情况(IIUC)
    【解决方案2】:

    所以我继续执行 Martin 的建议。这是我想出的 bash 脚本。

    pid_file=/path/to/pidfile
    port_file=/path/to/port_file
    old_pid=`cat $pid_file`
    
    if [[ -f $port_file ]]; then
        last_port=`cat $port_file`
        port_to_use=$(($last_port + 1))
    else
        port_to_use=8000
    fi
    
    # Reset so me don't go up forever
    if [[ $port_to_use -gt 8999 ]]; then
        port_to_use=8000
    fi
    
    sed -i "s/$old_port/$port_to_use/g" /path/to/nginx.conf
    
    python manage.py runfcgi host=127.0.0.1 port=$port_to_use maxchildren=5 maxspare=5 minspare=2 method=prefork pidfile=$pid_file
    
    echo $port_to_use > $port_file
    
    kill -HUP `cat /var/run/nginx.pid`
    
    echo "Sleeping for 5 seconds"
    sleep 5s
    
    echo "Killing old processes on $last_port, pid $old_pid"
    kill $old_pid
    

    【讨论】:

    • 哇,简单但非常有用。非常感谢分享这个!
    • 我认为脚本的sed 部分有错字,我猜$old_port 应该是$last_port
    【解决方案3】:

    我在寻找此问题的解决方案时遇到了此页面。其他一切都失败了,所以我查看了源代码:)

    解决方案似乎要简单得多。 Django fcgi 服务器使用 Flup,它以正确的方式处理 HUP 信号:它优雅地关闭。所以你所要做的就是:

    1. 向 fcgi 服务器发送 HUP 信号(runserver 的 pidfile= 参数会派上用场)

    2. 稍等(flup 允许子进程处理 10 秒,所以再等几个;15 看起来不错)

    3. 向 fcgi 服务器发送 KILL 信号,以防万一被阻止

    4. 再次启动服务器

    就是这样。

    【讨论】:

    • 如果您在upstart 下设置您的django fcgi 服务器,这将非常有效。 initctl reload <job> 将发送 HUP,您的作业定义中的 respawn 指令将处理重新启动。没有麻烦,没有大惊小怪。
    【解决方案4】:

    您可以使用生成而不是 FastCGI

    http://www.eflorenzano.com/blog/post/spawning-django/

    【讨论】:

      【解决方案5】:

      我们终于找到了正确的解决方案!

      http://rambleon.usebox.net/post/3279121000/how-to-gracefully-restart-django-running-fastcgi

      首先向flup发送一个HUP信号来表示重启。然后 Flup 将对它的所有子代执行此操作:

      1. 关闭将停止非活动子级的套接字
      2. 发送一个 INT 信号
      3. 等待 10 秒
      4. 发送 KILL 信号

      当所有的孩子都走了,它会开始新的。

      这几乎在所有时间都有效,除非当flup 执行步骤2 时孩子正在处理请求,那么您的服务器将因KeyboardInterrupt 而死,给用户一个500 错误。

      解决方案是安装一个 SIGINT 处理程序 - 有关详细信息,请参阅上面的页面。即使只是忽略 SIGINT 也会让您的进程有 10 秒的时间退出,这对于大多数请求来说已经足够了。

      【讨论】:

      • 正如该帖子中的 cmets 所述,这仅适用于 Flup 1.0.3。此外,我无法使用 prefork 进行此操作,只能使用线程。
      • 是否有不使用flup 1.0.3的问题或特殊原因?我在 prefork 模式下使用它,它工作正常。
      猜你喜欢
      • 2012-04-03
      • 2012-07-24
      • 2011-02-05
      • 2017-01-20
      • 1970-01-01
      • 2021-09-18
      • 2012-08-05
      • 2013-12-13
      • 1970-01-01
      相关资源
      最近更新 更多