Files
YG_FT_Platform/start_all.py

305 lines
7.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
YG_FT_Base 统一启动脚本
同时启动后端 API 服务和 Web 静态服务器
使用方法:
python start_all.py start # 启动所有服务
python start_all.py stop # 停止所有服务
python start_all.py restart # 重启所有服务
python start_all.py status # 查看服务状态
"""
import os
import sys
import yaml
import signal
import subprocess
import time
from pathlib import Path
# 获取脚本所在目录
SCRIPT_DIR = Path(__file__).parent.absolute()
CONFIG_FILE = SCRIPT_DIR / "config.yaml"
WEB_DIR = SCRIPT_DIR / "web"
# PID 文件路径
API_PID_FILE = SCRIPT_DIR / ".api.pid"
WEB_PID_FILE = SCRIPT_DIR / ".web.pid"
def load_config():
"""加载配置文件"""
if not CONFIG_FILE.exists():
print(f"❌ 配置文件不存在: {CONFIG_FILE}")
sys.exit(1)
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
app_config = config.get('app', {})
return {
'api_port': app_config.get('port', 7861),
'web_port': app_config.get('web_port', 7862)
}
def is_port_in_use(port):
"""检查端口是否被占用"""
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(('localhost', port)) == 0
def get_pid(pid_file):
"""获取 PID"""
if pid_file.exists():
try:
return int(pid_file.read_text().strip())
except:
return None
return None
def is_process_running(pid):
"""检查进程是否在运行"""
try:
os.kill(pid, 0)
return True
except OSError:
return False
def kill_process(pid_file):
"""杀死进程"""
pid = get_pid(pid_file)
if pid and is_process_running(pid):
try:
os.kill(pid, signal.SIGTERM)
time.sleep(0.5)
# 如果还在运行,强制杀死
if is_process_running(pid):
os.kill(pid, signal.SIGKILL)
return True
except OSError:
pass
pid_file.unlink(missing_ok=True)
return False
def check_virtual_env():
"""检查虚拟环境是否存在"""
venv_path = SCRIPT_DIR / "B_venv"
if not venv_path.exists():
print("⚠️ 虚拟环境不存在,请先运行 create_venv.sh 创建")
return False
return True
def start_api():
"""启动后端 API 服务"""
config = load_config()
api_port = config['api_port']
print("🚀 启动后端 API 服务...")
if not check_virtual_env():
return False
if is_port_in_use(api_port):
print(f"❌ 端口 {api_port} 已被占用,后端服务可能已在运行")
return False
venv_python = SCRIPT_DIR / "B_venv" / "bin" / "python"
main_py = SCRIPT_DIR / "src" / "main.py"
if not venv_python.exists():
venv_python = SCRIPT_DIR / "B_venv" / "Scripts" / "python.exe" # Windows
if not main_py.exists():
print(f"❌ 找不到主程序文件: {main_py}")
return False
# 启动进程
env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1'
proc = subprocess.Popen(
[str(venv_python), str(main_py)],
cwd=str(SCRIPT_DIR),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env
)
# 写入 PID
with open(API_PID_FILE, 'w') as f:
f.write(str(proc.pid))
# 等待服务启动
time.sleep(2)
if proc.poll() is not None:
print("❌ 后端服务启动失败")
API_PID_FILE.unlink(missing_ok=True)
return False
print(f"✅ 后端服务已启动 (PID: {proc.pid}, 端口: {api_port})")
return True
def start_web():
"""启动 Web 静态服务器"""
config = load_config()
web_port = config['web_port']
print("🚀 启动 Web 静态服务器...")
# 从项目根目录启动,这样 /lib/... 路径可以正确映射到 $PROJECT/lib/...
web_root = SCRIPT_DIR
if not web_root.exists():
print(f"❌ Web 目录不存在: {web_root}")
return False
if is_port_in_use(web_port):
print(f"⚠️ 端口 {web_port} 已被占用Web 服务可能已在运行")
return False
# 使用 python 启动简单 HTTP 服务器(从项目根目录)
env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1'
proc = subprocess.Popen(
[sys.executable, '-m', 'http.server', str(web_port)],
cwd=str(web_root),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env
)
# 写入 PID
with open(WEB_PID_FILE, 'w') as f:
f.write(str(proc.pid))
time.sleep(1)
if proc.poll() is not None:
print("❌ Web 服务启动失败")
WEB_PID_FILE.unlink(missing_ok=True)
return False
print(f"✅ Web 服务已启动 (PID: {proc.pid}, 端口: {web_port})")
return True
def stop_all():
"""停止所有服务"""
print("🛑 停止所有服务...")
# 停止后端服务
if kill_process(API_PID_FILE):
print("✅ 后端服务已停止")
# 停止 Web 服务
if kill_process(WEB_PID_FILE):
print("✅ Web 服务已停止")
# 清理残留进程
subprocess.run(['pkill', '-f', 'src/main.py'], capture_output=True)
config = load_config()
subprocess.run(['pkill', '-f', f'http.server {config["web_port"]}'], capture_output=True)
def show_status():
"""显示服务状态"""
config = load_config()
api_port = config['api_port']
web_port = config['web_port']
print("\n📊 服务状态:")
print("-" * 40)
api_pid = get_pid(API_PID_FILE)
if api_pid and is_process_running(api_pid):
print(f"✅ 后端 API: 运行中 (PID: {api_pid}, 端口: {api_port})")
else:
print(f"❌ 后端 API: 未运行 (端口: {api_port})")
web_pid = get_pid(WEB_PID_FILE)
if web_pid and is_process_running(web_pid):
print(f"✅ Web 服务: 运行中 (PID: {web_pid}, 端口: {web_port})")
else:
print(f"❌ Web 服务: 未运行 (端口: {web_port})")
print("-" * 40)
print("\n🌐 访问地址:")
print(f" - 后端 API: http://localhost:{api_port}")
print(f" - Web 页面: http://localhost:{web_port}/web/pages/main.html")
print()
def main():
if len(sys.argv) < 2:
print("用法: python start_all.py {start|stop|restart|status}")
print()
print("命令:")
print(" start - 启动所有服务")
print(" stop - 停止所有服务")
print(" restart - 重启所有服务")
print(" status - 查看服务状态")
sys.exit(1)
command = sys.argv[1].lower()
print("====================================")
print("YG_FT_Base 统一启动脚本 (Python版)")
print("====================================")
print()
config = load_config()
print(f"📦 端口配置:")
print(f" - 后端 API: {config['api_port']}")
print(f" - Web 服务: {config['web_port']}")
print()
if command == 'start':
start_api()
start_web()
print()
print("====================================")
print("所有服务已启动!")
print("====================================")
show_status()
elif command == 'stop':
stop_all()
print("✅ 所有服务已停止")
elif command == 'restart':
stop_all()
time.sleep(1)
start_api()
start_web()
print()
print("====================================")
print("所有服务已重启!")
print("====================================")
show_status()
elif command == 'status':
show_status()
else:
print(f"❌ 未知命令: {command}")
print("用法: python start_all.py {start|stop|restart|status}")
sys.exit(1)
if __name__ == '__main__':
main()