Поймать сигналы, отправленные runit (sv stop <id>)
У меня есть программа на Python, которая выполняет несколько рабочих процессов. Поскольку это должно быть обработано правильно, чтобы избежать потерянных процессов, я реализовал обработчик сигналов для отключения всех рабочих процессов.
Программа запускается примерно так:
- Запуск процесса пула (запускается X количество рабочих)
- Регистрировать обработчики сигналов (
signal.signal(signal.SIGTERM, my_signal_handler)
). Я также добавляю еще один обработчик сигнала дляSIGINT
с тем же обработчиком. - Запустите отдельный бэкэнд опроса потоков (базу данных) и добавьте задачи в пул процессов.
- В основном потоке опросить пул процессов для получения результатов (есть результат
multiprocessing.Queue
что отдельные работники добавляют результаты).
Идея состоит в том, что два отдельных потока, начатые в 3 и 4, поддерживают выполнение задач через механизм.
Если я начну это вручную и позвоню kill -15 <pid>
или же kill -2 <pid>
он корректно закрывает все, ждет процессов join()
, Читая из документации, runit отправляет TERM
к процессу, а затем CONT
, Однако, запустив это под runit, он просто показывает стандарт ok: down: <my_program>: 1s, normally up
, но процесс все еще работает в фоновом режиме (даже основной процесс, он НЕ УТВЕРЖДЕН).
Если я потом выйду и вручную убью процесс, я смогу увидеть в файле журнала, что он корректно завершает работу. Что я делаю неправильно? Кажется, что runit ТОЛЬКО убивает трехстрочный скрипт, который я создал для активации virtualenv, но оставляет фактический процесс python позади.
Даже если я запускаю скрипт "run" напрямую, я могу запустить kill
или Ctrl+C (так же, как SIGINT
) и он выключается правильно.
1 ответ
Итак, после некоторого обширного тестирования я понял это.
Runit отправит сигнал уничтожения run
скрипт, который не распространяет его по умолчанию. Вам нужно убедиться, что вы звоните exec python yourscript.py
в конце. Точно так же, если ваш run
скрипт вызывает другой скрипт оболочки (т.е. тот, который активирует ваш virtualenv или аналогичный), он должен делать это с exec
также.
Образцы:
run
:
#!/bin/sh
umask 002
2>&1
exec chpst -uanalytics cliscript router
cliscript
:
#!/bin/sh
# Resolve script path, assuming that the script resides in $(ABSPATH)/bin
SCRIPTPATH="$0"
if [ -h "$SCRIPTPATH" ]; then
SCRIPTPATH=$(readlink -e "$0")
fi
ABSPATH=$(dirname "$(cd "$(dirname "$SCRIPTPATH")"; pwd -L)")
# Load the virtual environment
source "$ABSPATH/venv/bin/activate"
# Set up environment
export PYTHONUNBUFFERED=1
exec python "$ABSPATH/bin/processing-cli.py" $@
Принять к сведению exec
вызывается, когда мы "передаем" управление следующему скрипту или самому питону.