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() |