如何优雅地重启 django 在 nginx 后面运行 fcgi?
Posted
技术标签:
【中文标题】如何优雅地重启 django 在 nginx 后面运行 fcgi?【英文标题】:How to gracefully restart django running fcgi behind nginx? 【发布时间】:2010-09-26 14:04:28 【问题描述】:我正在使用 fcgi 连接的 nginx 后面运行一个 django 实例(通过使用 manage.py runfcgi 命令)。由于代码已加载到内存中,我无法在不杀死并重新启动 django fcgi 进程的情况下重新加载新代码,从而中断实时网站。重启本身非常快。但是通过首先杀死 fcgi 进程,一些用户的操作会被打断,这是不好的。 我想知道如何重新加载新代码而不会造成任何中断。建议将不胜感激!
【问题讨论】:
【参考方案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 信号:它优雅地关闭。所以你所要做的就是:
向 fcgi 服务器发送 HUP 信号(runserver 的 pidfile= 参数会派上用场)
稍等(flup 允许子进程处理 10 秒,所以再等几个;15 看起来不错)
向 fcgi 服务器发送 KILL 信号,以防万一被阻止
再次启动服务器
就是这样。
【讨论】:
如果您在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 将对它的所有子代执行此操作:
-
关闭将停止非活动子级的套接字
发送一个 INT 信号
等待 10 秒
发送 KILL 信号
当所有的孩子都走了,它会开始新的。
这几乎在所有时间都有效,除非当flup 执行步骤2
时孩子正在处理请求,那么您的服务器将因KeyboardInterrupt
而死,给用户一个500 错误。
解决方案是安装一个 SIGINT 处理程序 - 有关详细信息,请参阅上面的页面。即使只是忽略 SIGINT 也会让您的进程有 10 秒的时间退出,这对于大多数请求来说已经足够了。
【讨论】:
正如该帖子中的 cmets 所述,这仅适用于 Flup 1.0.3。此外,我无法使用 prefork 进行此操作,只能使用线程。 是否有不使用flup 1.0.3的问题或特殊原因?我在 prefork 模式下使用它,它工作正常。以上是关于如何优雅地重启 django 在 nginx 后面运行 fcgi?的主要内容,如果未能解决你的问题,请参考以下文章