VWED_server/packaging/service_manager.py

284 lines
9.4 KiB
Python
Raw Normal View History

2025-09-09 10:41:27 +08:00
#!/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()