Защиту от запуска нескольких копий программы мне срочно понадобилась когда я решил запилить телеграм-бота. Поскольку бот должен работать 24/7 без перерывов на обед, встал вопрос автозапуска и контроля работы бота. Работать бот должен бесперебойно. Автозапуск не решал задачи ибо автозапуск происходит при старте системы, а дальше может произойти сбой и процесс бота остановится. За этим кто-то должен следить. Вопрос: кто? Ответ: да сам бот пускай и следит. Первый мой способ, описанный ниже, не дал ожидаемого результата.
Логика работы
Логика работы проверки наличия запущенного процесса строится на двух моментах:
- Файл с pid’ом
- Поиск процесса с pid’ом из файла
То есть, когда запускается процесс впервые, то создается файл с идентификатором процесса. Когда происходит повторный запуск, то программа извлекает идентификатор из файла, проверяет есть ли процесс таким идентификатором и если такой процесс есть, завершает свою работу, если нет, пишет в файл свой pid и продолжает свою работу.
Реализация
import os, psutil, sys
pid_file_path = '/dev/shm/pybot.pid'
if os.path.exists( pid_file_path ):
pid_file = open( pid_file_path, "r" )
pid = int( pid_file.read() )
pid_file.close()
if psutil.pid_exists( pid ):
print( 'Бот уже запущен' )
sys.exit()
pid_file = open( pid_file_path, "w" )
pid_file.write( str( os.getpid() ) )
pid_file.close()
Файл с идентификатором процесса пишется в оперативную память (dev/shm). Во-первых: это быстрее чем писать на диск, во-вторых: при перезагрузке этот файл исчезнет.
Далее вешаем в crontab задачу и все. В моем случае это запуск программы каждую минуту. Если бот запущен, программа моментально отвалится. Если по какой-то причине бот схватил ошибку и вырубился, то cron запустит его снова. Таким образом мы одним махом получаем контроль над процессом и его бесперебойной работы.
Через какое-то время я замечал что запущено два и более процесса, что нарушало работу бота. Поскольку этот способ не дал ожидаемого результата. И начал искать. Совсем случайно я наткнулся на готовое решение.
Модуль Tendo
Безотказное решение. Не знаю на чем основана его логика, но работает безотказно и проверено многочасовыми аптаймами. Как в аптеке — процесс всегда один. А добавление в проект — элементарно.
В начало файла надо вставить код из нескольких строк:
from tendo import singleton
try:
me = singleton.SingleInstance()
except:
sys.exit()
Вот и все. При попытке запуска второго процесса, он будет завершаться и таким образом будет работать только один процесс.