284 lines
9.4 KiB
Python
284 lines
9.4 KiB
Python
|
#!/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()
|