VWED_server/packaging/service_manager.py
2025-09-09 10:41:27 +08:00

284 lines
9.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.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED任务系统服务管理工具
用于管理VWED服务的启动、停止、状态检查等操作
"""
import os
import sys
import time
import signal
import psutil
import argparse
import subprocess
from pathlib import Path
class VWEDServiceManager:
"""VWED服务管理器"""
def __init__(self):
self.service_name = "VWED_Task_System.exe"
self.pid_file = Path("vwed_service.pid")
self.log_file = Path("vwed_service.log")
self.config_file = Path("config.ini")
def start_service(self, headless=True, wait_for_start=True):
"""启动服务"""
if self.is_running():
print("服务已在运行中")
return True
print("启动VWED服务...")
# 构建启动命令
cmd = [self.service_name]
if headless:
cmd.append("--headless")
try:
# 启动服务进程
if headless:
# 后台模式,重定向输出到日志文件
with open(self.log_file, 'a', encoding='utf-8') as log_f:
process = subprocess.Popen(
cmd,
stdout=log_f,
stderr=subprocess.STDOUT,
cwd=Path.cwd(),
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
)
else:
# 前台模式
process = subprocess.Popen(cmd, cwd=Path.cwd())
# 记录PID
with open(self.pid_file, 'w') as f:
f.write(str(process.pid))
print(f"服务已启动 (PID: {process.pid})")
if wait_for_start:
# 等待服务完全启动
print("等待服务启动完成...")
for i in range(30): # 最多等待30秒
if self._check_service_port():
print("服务启动成功,可以访问 http://localhost:8000")
return True
time.sleep(1)
print("警告: 服务可能未完全启动,请检查日志文件")
return True
except FileNotFoundError:
print(f"错误: 找不到可执行文件 {self.service_name}")
return False
except Exception as e:
print(f"启动服务失败: {e}")
return False
def stop_service(self, force=False):
"""停止服务"""
if not self.is_running():
print("服务未运行")
self._cleanup_pid_file()
return True
try:
pid = self._get_pid()
if pid:
print(f"正在停止服务 (PID: {pid})...")
try:
process = psutil.Process(pid)
if not force:
# 优雅关闭
process.terminate()
# 等待进程结束
try:
process.wait(timeout=10)
print("服务已优雅停止")
except psutil.TimeoutExpired:
print("优雅停止超时,强制停止服务...")
process.kill()
print("服务已强制停止")
else:
# 强制关闭
process.kill()
print("服务已强制停止")
except psutil.NoSuchProcess:
print("进程不存在清理PID文件")
self._cleanup_pid_file()
return True
except Exception as e:
print(f"停止服务失败: {e}")
return False
def restart_service(self, headless=True):
"""重启服务"""
print("重启服务...")
if self.is_running():
if not self.stop_service():
return False
# 等待进程完全停止
time.sleep(2)
return self.start_service(headless)
def service_status(self, show_logs=True):
"""检查服务状态"""
print("检查VWED服务状态...")
print("-" * 50)
# 检查进程状态
if self.is_running():
pid = self._get_pid()
try:
process = psutil.Process(pid)
print(f"✓ 服务正在运行")
print(f" PID: {pid}")
print(f" 内存使用: {process.memory_info().rss / 1024 / 1024:.1f} MB")
print(f" CPU使用率: {process.cpu_percent():.1f}%")
print(f" 运行时间: {time.time() - process.create_time():.0f}")
except psutil.NoSuchProcess:
print("✗ 进程不存在清理PID文件")
self._cleanup_pid_file()
return False
else:
print("✗ 服务未运行")
# 检查端口状态
if self._check_service_port():
print("✓ 服务端口 8000 可访问")
print(" 访问地址: http://localhost:8000")
else:
print("✗ 服务端口 8000 不可访问")
# 检查配置文件
if self.config_file.exists():
print("✓ 配置文件存在")
else:
print("✗ 配置文件不存在")
# 显示最近日志
if show_logs and self.log_file.exists():
print("\n最近的日志内容:")
print("-" * 30)
try:
with open(self.log_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines[-10:]: # 显示最后10行
print(line.strip())
except Exception as e:
print(f"读取日志失败: {e}")
return self.is_running()
def is_running(self):
"""检查服务是否运行中"""
pid = self._get_pid()
if not pid:
return False
try:
return psutil.pid_exists(pid)
except:
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 (ValueError, FileNotFoundError):
return None
def _cleanup_pid_file(self):
"""清理PID文件"""
if self.pid_file.exists():
try:
self.pid_file.unlink()
except:
pass
def _check_service_port(self):
"""检查服务端口是否可访问"""
try:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1)
result = s.connect_ex(('localhost', 8000))
return result == 0
except:
return False
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='VWED任务系统服务管理工具')
parser.add_argument('action', choices=['start', 'stop', 'restart', 'status', 'logs'],
help='操作: start(启动), stop(停止), restart(重启), status(状态), logs(查看日志)')
parser.add_argument('--headless', action='store_true',
help='后台模式运行适用于start和restart')
parser.add_argument('--force', action='store_true',
help='强制停止服务适用于stop')
parser.add_argument('--no-wait', action='store_true',
help='启动后不等待服务完全启动适用于start')
args = parser.parse_args()
manager = VWEDServiceManager()
try:
if args.action == 'start':
success = manager.start_service(
headless=args.headless,
wait_for_start=not args.no_wait
)
sys.exit(0 if success else 1)
elif args.action == 'stop':
success = manager.stop_service(force=args.force)
sys.exit(0 if success else 1)
elif args.action == 'restart':
success = manager.restart_service(headless=args.headless)
sys.exit(0 if success else 1)
elif args.action == 'status':
is_running = manager.service_status()
sys.exit(0 if is_running else 1)
elif args.action == 'logs':
if manager.log_file.exists():
try:
with open(manager.log_file, 'r', encoding='utf-8') as f:
print(f.read())
except Exception as e:
print(f"读取日志失败: {e}")
sys.exit(1)
else:
print("日志文件不存在")
sys.exit(1)
except KeyboardInterrupt:
print("\n操作被用户中断")
sys.exit(1)
except Exception as e:
print(f"操作失败: {e}")
sys.exit(1)
if __name__ == "__main__":
main()