Хотя этот вопрос помечен как EventMachine, мы также приветствуем общие решения для сокетов BSD на любом языке.
Немного предыстории:
У меня есть приложение, которое прослушивает TCP-сокет. Он запускается и завершается с помощью обычного сценария инициализации в стиле System V.
Моя проблема в том, что ему нужно некоторое время для запуска, прежде чем он будет готов обслуживать сокет TCP. Это не слишком долго, возможно, всего 5 секунд, но это на 5 секунд больше, если перезапуск необходимо выполнить в течение рабочего дня. Также важно, чтобы существующие соединения оставались открытыми и завершались нормально.
Причины перезапуска приложения - исправления, обновления и тому подобное. К сожалению, я оказываюсь в положении, когда время от времени мне приходится делать такие вещи в продакшене.
Вопрос:
Я ищу способ аккуратно передать прослушивающий сокет TCP от одного процесса к другому и в результате получить лишь долю секунды простоя. Я бы хотел, чтобы существующие соединения / сокеты оставались открытыми и завершали обработку в старом процессе, в то время как новый процесс начинает обслуживать новые подключения.
Есть ли какой-нибудь проверенный способ сделать это с помощью BSD-сокетов? (Бонусные баллы за решение EventMachine.)
Возможно, существуют библиотеки с открытым исходным кодом, реализующие это, которые я могу использовать как есть или использовать в качестве справочника? (Опять же, приветствуются и решения, не связанные с Ruby и EventMachine!)
2 ответа
Есть несколько способов сделать это без простоев, с соответствующими изменениями в серверной программе.
Один из них - реализовать возможность перезапуска на самом сервере, например, при получении определенного сигнала или другого сообщения. Затем программа выполнит свою новую версию, передав ей номер файлового дескриптора прослушивающего сокета, например. как аргумент. У этого сокета должен быть снят флажок FD_CLOEXEC
( значение по умолчанию), чтобы он был унаследован. Поскольку другие сокеты будут продолжать обслуживаться исходным процессом и не должны передаваться новому процессу, флаг должен быть установлен на них, например. используя fcntl()
. После разветвления и выполнения нового процесса исходный процесс может продолжить и закрыть прослушивающий сокет без какого-либо прерывания службы, поскольку новый процесс теперь прослушивает этот сокет.
Альтернативный метод, если вы не хотите, чтобы старому серверу приходилось разветвлять и запускать сам новый сервер, было бы использование сокет домена Unix для связи между старым и новым серверным процессом. При запуске новый серверный процесс может проверять наличие такого сокета в хорошо известном месте файловой системы. Если присутствует, новый сервер подключится к этому сокету и запросит, чтобы старый сервер передал свой слушающий сокет в качестве вспомогательных данных с помощью SCM_RIGHTS. Пример этого приведен в конце cmsg. (3).
SCM_RIGHTS
звучит аккуратно. Я попробую завтра.
UNIXSocket#send_io
. Он отправляет единственный файловый дескриптор и нулевой байт. По-видимому, другого пути нет, и получение класса UNIXSocket из EventMachine также невозможно. (Возможно, я взломаю это.) Таким образом, в EventMachine это можно сделать с помощью ручной магии сокетов и EventMachine::watch
. Спасибо за указатель.
Жан-Поль Кальдероне написал подробную презентацию в 2004 году о комплексном решении вашей проблемы с использованием Twisted, включая миграцию сокетов и другие проблемы.
Похожие вопросы
Новые вопросы
sockets
Конечная точка двунаправленного межпроцессного взаимодействия. Это часто относится к потоку процессов через сетевое соединение, но ни в коем случае не ограничивается этим. Не следует путать с WebSocket (протоколом) или другими абстракциями (например, socket.io).