Как можно обновить Nginx, не отбрасывая никаких запросов? - PullRequest
4 голосов
/ 18 марта 2011

Согласно документации Nginx :

Если вам нужно заменить двоичный файл nginx с новым (при обновлении до новая версия или добавление / удаление сервера модули), вы можете сделать это без каких-либо время простоя сервиса - нет входящего запросы будут потеряны.

Мой коллега и я пытались выяснить: как это работает? . Мы знаем (мы думаем), что:

  • Только один процесс может прослушивать порт 80 одновременно
  • Nginx создает сокет и подключает его к порту 80
  • Родительский процесс и любой его дочерний элемент могут связываться с одним и тем же сокетом, так как Nginx может иметь несколько дочерних рабочих, отвечающих на запросы

Мы также провели несколько экспериментов с Nginx, например:

  • Отправить kill -USR2 текущему основному процессу
  • Повторно запускайте ps -ef | grep unicorn, чтобы увидеть любые процессы единорога с их собственными pids и их родительскими pids
  • Заметьте, что новый мастер-процесс сначала является дочерним по отношению к старому мастер-процессу, но когда старый мастер-процесс исчез, новый мастер-процесс имеет ppid 1.

Так что, очевидно, новый мастер-процесс может прослушивать тот же сокет, что и старый, в то время как они оба работают, потому что в то время новый мастер является дочерним по отношению к старому мастеру. Но каким-то образом новый мастер-процесс может стать ... хм ... ничьим ребенком?

Я предполагаю, что это стандартные вещи Unix, но мое понимание процессов, портов и сокетов чертовски размыто . Кто-нибудь может объяснить это более подробно? Являются ли какие-либо из наших предположений неверными? И есть ли книга, которую я могу прочитать, чтобы по-настоящему заняться этим?

Ответы [ 2 ]

4 голосов
/ 21 марта 2011

Для деталей: http://www.csc.villanova.edu/~mdamian/Sockets/TcpSockets.htm описывает библиотеку C для сокетов TCP.

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

Итак, вот поток. Nginx, запускается нормально:

  1. Вызывает socket (), bind () и listen () для установки сокета, на который ссылается дескриптор файла (целое число).
  2. Запускает поток, который вызывает accept () для дескриптора файла в цикле для обработки входящих соединений.

Тогда Nginx разветвляется. Родитель продолжает работать как обычно, но ребенок немедленно запускает новый двоичный файл. exec () стирает старую программу, память и запущенные потоки, но наследует дескрипторы открытых файлов: см. http://linux.die.net/man/2/execve. Я подозреваю, что вызов exec () передает номер дескриптора открытого файла в качестве параметра командной строки.

Ребенок, начатый как часть обновления:

  1. Считывает номер дескриптора открытого файла из командной строки.
  2. Запускает поток, который вызывает accept () для дескриптора файла в цикле для обработки входящих соединений.
  3. Указывает родителю истощить (прекратить прием () и завершить существующие соединения) и умереть.
1 голос
/ 18 марта 2011

Я понятия не имею, как это делает nginx, но, в принципе, он может просто exec новый двоичный файл, содержащий слушающий сокет новый процесс (фактически, он остается тем же процессом, он просто заменяет программу, выполняющуюся в Это). Прослушивающий сокет имеет незавершенные входящие соединения, и, пока он достаточно быстр для загрузки, он должен иметь возможность начать их обработку до того, как он переполнится. Если нет, то он, вероятно, может сначала выполнить форк, выполнить exec и подождать, пока он загрузится до того момента, когда он будет готов обрабатывать входящие запросы, а затем передать команду прослушивающего сокета (дескрипторы файлов наследуются при разветвлении, оба имеют доступ это) через какой-то внутренний механизм, прежде чем выйти. Принимая во внимание ваши наблюдения, это похоже на то, что он делает (если ваш родительский процесс умирает, ваш ppid переназначается на init, т.е. pid 1)

Если у него есть несколько процессов, конкурирующих за прием в одном и том же сокете прослушивания (опять же, я понятия не имею, как это делает nginx, возможно, у него есть процесс диспетчеризации?), То вы можете заменить их один за другим, заказав их выполнить новую программу, как указано выше, но по одному, чтобы никогда не уронить мяч. Обратите внимание, что во время такого процесса никогда не будет никаких новых pids или изменений отношения родитель / потомок.

По крайней мере, я думаю, что, вероятно, я бы это сделал, с макушки головы.

...