Как изящно перезапустить django, запустив fcgi за nginx? - PullRequest
24 голосов
/ 12 декабря 2008

Я запускаю экземпляр django за nginx, подключенным с помощью fcgi (с помощью команды manage.py runfcgi). Так как код загружен в память, я не могу перезагрузить новый код, не убив и не перезапустив процессы django fcgi, таким образом прерывая живой сайт. Сам перезапуск очень быстрый. Но если сначала убить процессы fcgi, то некоторые действия пользователей будут прерваны, что не очень хорошо. Мне интересно, как я могу перезагрузить новый код без каких-либо прерываний. Советы будут высоко оценены!

Ответы [ 5 ]

15 голосов
/ 05 июня 2009

Так что я пошел дальше и реализовал предложение Мартина. Вот сценарий 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
15 голосов
/ 12 декабря 2008

Я бы запустил новый процесс fcgi на новом порту, изменил конфигурацию nginx для использования нового порта, настроил перезагрузку nginx (что само по себе изящно), а затем в конце остановил бы старый процесс (вы можете использовать netstat, чтобы найти когда последнее соединение со старым портом закрыто).

В качестве альтернативы, вы можете изменить реализацию fcgi, чтобы обработать новый процесс, закрыть все сокеты в дочернем процессоре, кроме сокета сервера fcgi, закрыть сокет сервера fcgi в родительском процессоре, выполнить новый процесс django в дочернем процессоре (используя сокет сервера fcgi) и завершите родительский процесс, как только все соединения fcgi будут закрыты. IOW, осуществить изящный перезапуск для runfcgi.

10 голосов
/ 22 июля 2009

Я наткнулся на эту страницу, когда искал решение этой проблемы. Все остальное не удалось, поэтому я посмотрел на исходный код:)

Решение кажется намного проще. Сервер Django fcgi использует flup, который правильно обрабатывает сигнал HUP: он корректно завершает работу. Так что все, что вам нужно сделать, это:

  1. отправка сигнала HUP серверу fcgi (пригодится аргумент pidfile = runserver)

  2. немного подождать (флоп позволяет дочерним процессам 10 секунд, поэтому подождите еще пару; 15 выглядит как хорошее число)

  3. отправил сигнал KILL на сервер fcgi, на случай, если что-то заблокировало его

  4. снова запустить сервер

Вот и все.

3 голосов
/ 17 января 2009

Вы можете использовать порождение вместо FastCGI

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

2 голосов
/ 14 февраля 2011

Мы наконец нашли правильное решение для этого!

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

Сначала отправьте сигнал HUP, чтобы сообщить о перезапуске. Затем Flup сделает это со всеми своими детьми:

  1. закрывает сокет, который остановит неактивных детей
  2. отправляет сигнал INT
  3. ждет 10 секунд
  4. отправляет сигнал KILL

Когда все дети уйдут, у них появятся новые.

Это работает почти все время, за исключением того, что если ребенок обрабатывает запрос, когда flup выполняет шаг 2, то ваш сервер умрет с KeyboardInterrupt, что выдаст пользователю 500 ошибок.

Решением является установка обработчика SIGINT - подробности см. На странице выше. Даже простое игнорирование SIGINT дает вашему процессу 10 секунд для выхода, чего достаточно для большинства запросов.

...