Files
prod/infra/monitoring/pid_manager.py

162 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Модуль для управления PID файлами процессов
Общий модуль для всех ботов в проекте
"""
import os
import sys
import signal
import atexit
import logging
from typing import Optional
logger = logging.getLogger(__name__)
class PIDManager:
"""Класс для управления PID файлами"""
def __init__(self, pid_file_path: str, process_name: str = "process"):
"""
Инициализация PID менеджера
Args:
pid_file_path: Путь к PID файлу
process_name: Имя процесса для логирования
"""
self.pid_file_path = pid_file_path
self.process_name = process_name
self.pid = os.getpid()
def create_pid_file(self) -> bool:
"""
Создание PID файла с текущим PID процесса
Returns:
bool: True если файл создан успешно, False в противном случае
"""
try:
# Создаем директорию если не существует
pid_dir = os.path.dirname(self.pid_file_path)
if pid_dir and not os.path.exists(pid_dir):
os.makedirs(pid_dir, exist_ok=True)
# Записываем PID в файл
with open(self.pid_file_path, 'w') as f:
f.write(str(self.pid))
logger.info(f"PID файл создан для {self.process_name}: {self.pid_file_path} (PID: {self.pid})")
# Регистрируем функцию очистки при завершении
atexit.register(self.cleanup_pid_file)
# Регистрируем обработчики сигналов для корректной очистки
signal.signal(signal.SIGTERM, self._signal_handler)
signal.signal(signal.SIGINT, self._signal_handler)
return True
except Exception as e:
logger.error(f"Ошибка при создании PID файла для {self.process_name}: {e}")
return False
def cleanup_pid_file(self):
"""Удаление PID файла при завершении процесса"""
try:
if os.path.exists(self.pid_file_path):
os.remove(self.pid_file_path)
logger.info(f"PID файл удален для {self.process_name}: {self.pid_file_path}")
except Exception as e:
logger.error(f"Ошибка при удалении PID файла для {self.process_name}: {e}")
def _signal_handler(self, signum, frame):
"""Обработчик сигналов для корректного завершения"""
logger.info(f"Получен сигнал {signum} для {self.process_name}, очищаем PID файл...")
self.cleanup_pid_file()
sys.exit(0)
def is_running(self) -> bool:
"""
Проверка, запущен ли процесс с PID из файла
Returns:
bool: True если процесс запущен, False в противном случае
"""
try:
if not os.path.exists(self.pid_file_path):
return False
with open(self.pid_file_path, 'r') as f:
content = f.read().strip()
if not content:
return False
try:
pid = int(content)
# Проверяем, существует ли процесс с таким PID
os.kill(pid, 0) # Отправляем сигнал 0 для проверки существования
return True
except (ValueError, OSError):
# PID не валидный или процесс не существует
return False
except Exception as e:
logger.error(f"Ошибка при проверке PID файла для {self.process_name}: {e}")
return False
def get_pid(self) -> Optional[int]:
"""
Получение PID из файла
Returns:
int: PID процесса или None если файл не существует или невалидный
"""
try:
if not os.path.exists(self.pid_file_path):
return None
with open(self.pid_file_path, 'r') as f:
content = f.read().strip()
if not content:
return None
return int(content)
except (ValueError, FileNotFoundError) as e:
logger.error(f"Ошибка при чтении PID файла для {self.process_name}: {e}")
return None
def create_pid_manager(process_name: str, project_root: str = None) -> PIDManager:
"""
Создание PID менеджера для указанного процесса
Args:
process_name: Имя процесса (например, 'helper_bot', 'admin_bot', etc.)
project_root: Корневая директория проекта. Если None, определяется автоматически
Returns:
PIDManager: Экземпляр PID менеджера
"""
if project_root is None:
# Определяем корень проекта автоматически
current_file = os.path.abspath(__file__)
# Поднимаемся на 2 уровня вверх от infra/monitoring/pid_manager.py
project_root = os.path.dirname(os.path.dirname(current_file))
pid_file_path = os.path.join(project_root, f"{process_name}.pid")
return PIDManager(pid_file_path, process_name)
def get_bot_pid_manager(bot_name: str) -> PIDManager:
"""
Удобная функция для создания PID менеджера для ботов
Args:
bot_name: Имя бота (например, 'helper_bot', 'admin_bot', etc.)
Returns:
PIDManager: Экземпляр PID менеджера
"""
return create_pid_manager(bot_name)