Files
yarmarka/server_manager.py
2026-03-20 11:30:30 +03:00

230 lines
8.1 KiB
Python
Raw Permalink 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.
#!/usr/bin/env python3
# server_manager.py - Скрипт для управления сервером в фоновом режиме
import os
import sys
import signal
import subprocess
import time
import atexit
from pathlib import Path
# Конфигурация
PID_FILE = "server.pid"
LOG_FILE = "server.log"
ERROR_LOG = "server_error.log"
PYTHON_CMD = sys.executable
SERVER_SCRIPT = "server.py"
class ServerManager:
def __init__(self):
self.pid_file = Path(PID_FILE)
self.log_file = Path(LOG_FILE)
self.error_log = Path(ERROR_LOG)
def is_running(self):
"""Проверка, запущен ли сервер"""
if not self.pid_file.exists():
return False
try:
with open(self.pid_file, 'r') as f:
pid = int(f.read().strip())
# Проверяем, существует ли процесс
os.kill(pid, 0)
return True
except (OSError, ValueError, ProcessLookupError):
# PID файл существует, но процесс не найден
self.pid_file.unlink(missing_ok=True)
return False
def get_pid(self):
"""Получить PID сервера"""
if not self.pid_file.exists():
return None
try:
with open(self.pid_file, 'r') as f:
return int(f.read().strip())
except:
return None
def start(self):
"""Запуск сервера в фоновом режиме"""
if self.is_running():
print(f"❌ Сервер уже запущен (PID: {self.get_pid()})")
return False
print("🚀 Запуск сервера в фоновом режиме...")
try:
# Открываем файлы для логов
log_fd = open(self.log_file, 'a')
err_fd = open(self.error_log, 'a')
# Запускаем процесс
process = subprocess.Popen(
[PYTHON_CMD, SERVER_SCRIPT],
stdout=log_fd,
stderr=err_fd,
stdin=subprocess.DEVNULL,
start_new_session=True, # Отдельная сессия для фонового процесса
cwd=os.path.dirname(os.path.abspath(__file__))
)
# Сохраняем PID
with open(self.pid_file, 'w') as f:
f.write(str(process.pid))
# Небольшая задержка для проверки запуска
time.sleep(2)
if self.is_running():
print(f"✅ Сервер успешно запущен (PID: {process.pid})")
print(f"📝 Логи: {self.log_file}")
print(f"❌ Логи ошибок: {self.error_log}")
return True
else:
print("❌ Сервер не запустился")
return False
except Exception as e:
print(f"❌ Ошибка при запуске: {e}")
return False
def stop(self):
"""Остановка сервера"""
if not self.is_running():
print("❌ Сервер не запущен")
return False
pid = self.get_pid()
print(f"🛑 Остановка сервера (PID: {pid})...")
try:
# Отправляем SIGTERM
os.kill(pid, signal.SIGTERM)
# Ждем завершения
timeout = 10
for _ in range(timeout):
time.sleep(1)
if not self.is_running():
print("✅ Сервер остановлен")
self.pid_file.unlink(missing_ok=True)
return True
# Если не остановился, принудительно завершаем
print("⚠️ Сервер не отвечает, принудительное завершение...")
os.kill(pid, signal.SIGKILL)
time.sleep(1)
if not self.is_running():
print("✅ Сервер принудительно остановлен")
self.pid_file.unlink(missing_ok=True)
return True
else:
print("Не удалось остановить сервер")
return False
except ProcessLookupError:
print("⚠️ Процесс не найден, удаляем PID файл")
self.pid_file.unlink(missing_ok=True)
return True
except Exception as e:
print(f"❌ Ошибка при остановке: {e}")
return False
def restart(self):
"""Перезапуск сервера"""
print("🔄 Перезапуск сервера...")
if self.is_running():
self.stop()
time.sleep(2)
return self.start()
def status(self):
"""Статус сервера"""
if self.is_running():
pid = self.get_pid()
print(f"✅ Сервер запущен (PID: {pid})")
print(f"📝 Логи: {self.log_file} ({self.log_file.stat().st_size} bytes)")
print(
f"❌ Логи ошибок: {self.error_log} ({self.error_log.stat().st_size if self.error_log.exists() else 0} bytes)")
# Показываем последние строки лога
if self.log_file.exists():
print("\n📋 Последние строки лога:")
with open(self.log_file, 'r') as f:
lines = f.readlines()
for line in lines[-5:]:
print(f" {line.strip()}")
return True
else:
print("❌ Сервер не запущен")
return False
def logs(self, lines=20):
"""Показать последние строки лога"""
if not self.log_file.exists():
print("📝 Лог файл не найден")
return
print(f"📋 Последние {lines} строк лога:")
print("-" * 50)
with open(self.log_file, 'r') as f:
all_lines = f.readlines()
for line in all_lines[-lines:]:
print(line.strip())
def errors(self, lines=20):
"""Показать последние строки лога ошибок"""
if not self.error_log.exists():
print("❌ Лог ошибок не найден")
return
print(f"⚠️ Последние {lines} строк лога ошибок:")
print("-" * 50)
with open(self.error_log, 'r') as f:
all_lines = f.readlines()
for line in all_lines[-lines:]:
print(line.strip())
def main():
manager = ServerManager()
if len(sys.argv) < 2:
print("Использование:")
print(" python server_manager.py start - запустить сервер")
print(" python server_manager.py stop - остановить сервер")
print(" python server_manager.py restart - перезапустить сервер")
print(" python server_manager.py status - статус сервера")
print(" python server_manager.py logs - показать логи")
print(" python server_manager.py errors - показать ошибки")
return
command = sys.argv[1].lower()
if command == 'start':
manager.start()
elif command == 'stop':
manager.stop()
elif command == 'restart':
manager.restart()
elif command == 'status':
manager.status()
elif command == 'logs':
lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20
manager.logs(lines)
elif command == 'errors':
lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20
manager.errors(lines)
else:
print(f"❌ Неизвестная команда: {command}")
if __name__ == "__main__":
main()