改造启动方式为一键启动所有服务
This commit is contained in:
247
main.py
247
main.py
@@ -1,3 +1,6 @@
|
|||||||
|
import signal
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
@@ -18,79 +21,6 @@ LoggerManager.setup_logging()
|
|||||||
logger = get_main_logger()
|
logger = get_main_logger()
|
||||||
|
|
||||||
|
|
||||||
def start_celery_worker():
|
|
||||||
"""启动 Celery Worker"""
|
|
||||||
try:
|
|
||||||
logger.info("🌿 启动Celery Worker...")
|
|
||||||
|
|
||||||
# 构建Worker启动命令 - 独立进程启动
|
|
||||||
worker_cmd = [
|
|
||||||
sys.executable, "-m", "celery",
|
|
||||||
"-A", "app.celery_app", # 指定celery应用模块
|
|
||||||
"worker",
|
|
||||||
'--loglevel=info',
|
|
||||||
'--pool=solo',
|
|
||||||
'--concurrency=1',
|
|
||||||
'--time-limit=300', # 5分钟任务超时
|
|
||||||
'--soft-time-limit=240' # 4分钟软超时
|
|
||||||
]
|
|
||||||
subprocess.run(worker_cmd, check=True)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ Celery Worker 启动失败: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def start_celery_beat():
|
|
||||||
"""启动 Celery Beat 调度器"""
|
|
||||||
try:
|
|
||||||
logger.info("📅 启动Celery Beat调度器...")
|
|
||||||
|
|
||||||
# 使用跨平台的调度文件路径
|
|
||||||
import tempfile
|
|
||||||
import os
|
|
||||||
schedule_file = os.path.join(tempfile.gettempdir(), 'celerybeat-schedule')
|
|
||||||
|
|
||||||
# 构建Beat启动命令 - 独立进程启动
|
|
||||||
beat_cmd = [
|
|
||||||
sys.executable, "-m", "celery",
|
|
||||||
"-A", "app.celery_app", # 指定celery应用模块
|
|
||||||
"beat",
|
|
||||||
'--loglevel=info',
|
|
||||||
f'--schedule={schedule_file}'
|
|
||||||
]
|
|
||||||
|
|
||||||
subprocess.run(beat_cmd, check=True)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ Celery Beat 启动失败: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def start_flower():
|
|
||||||
"""启动 Flower 监控服务"""
|
|
||||||
try:
|
|
||||||
logger.info("📊 启动Flower监控服务...")
|
|
||||||
|
|
||||||
# 构建Flower启动命令 - 独立进程启动
|
|
||||||
flower_cmd = [
|
|
||||||
sys.executable, "-m", "celery",
|
|
||||||
"-A", "app.celery_app", # 指定celery应用模块
|
|
||||||
f"--broker={settings.celery_broker_url}",
|
|
||||||
"flower",
|
|
||||||
f"--port={settings.flower_port}"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 添加基础认证(如果配置了)
|
|
||||||
if settings.flower_basic_auth:
|
|
||||||
flower_cmd.append(f"--basic_auth={settings.flower_basic_auth}")
|
|
||||||
|
|
||||||
# 添加URL前缀(如果配置了)
|
|
||||||
if settings.flower_url_prefix:
|
|
||||||
flower_cmd.append(f"--url_prefix={settings.flower_url_prefix}")
|
|
||||||
|
|
||||||
subprocess.run(flower_cmd, check=True)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ Flower 监控服务启动失败: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
# 启动时初始化
|
# 启动时初始化
|
||||||
@@ -211,55 +141,134 @@ async def health_check():
|
|||||||
return {"status": "healthy"}
|
return {"status": "healthy"}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
# 全局变量存储进程
|
||||||
import uvicorn
|
processes = []
|
||||||
import argparse
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="AI Talk Callback API")
|
def signal_handler(signum, frame):
|
||||||
parser.add_argument("--mode", choices=["api", "worker", "beat", "flower"],
|
"""信号处理器,用于优雅关闭所有服务"""
|
||||||
help="启动模式: api(仅API), worker(仅Celery Worker), beat(仅Celery Beat), flower(仅Flower监控)")
|
print(f"\n🛑 接收到信号 {signum},正在关闭所有服务...")
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# 如果没有传递任何参数,输出完整的提示信息
|
# 逆序关闭进程(最后启动的最先关闭)
|
||||||
if not args.mode:
|
for i, process in enumerate(reversed(processes)):
|
||||||
print("🚀 AI Talk Callback API 管理指南")
|
if process and process.poll() is None: # 进程仍在运行
|
||||||
print("=" * 50)
|
print(f"🔄 正在关闭进程 {len(processes) - i}...")
|
||||||
print("\n📋 启动模式:")
|
try:
|
||||||
print(" api - 启动 FastAPI Web 应用服务 (端口: 8000)")
|
process.terminate() # 发送 SIGTERM 信号
|
||||||
print(" worker - 启动 Celery Worker 任务处理器")
|
process.wait(timeout=10) # 等待最多10秒
|
||||||
print(" beat - 启动 Celery Beat 定时任务调度器")
|
print(f"✅ 进程已关闭")
|
||||||
print(" flower - 启动 Flower 监控服务")
|
except subprocess.TimeoutExpired:
|
||||||
print("\n🔧 使用示例:")
|
print(f"⚠️ 进程未在10秒内响应,强制关闭...")
|
||||||
print(" python main.py --mode=api # 启动 Web API 服务")
|
process.kill() # 强制杀死进程
|
||||||
print(" python main.py --mode=worker # 启动任务处理器")
|
except Exception as e:
|
||||||
print(" python main.py --mode=beat # 启动定时任务调度器")
|
print(f"❌ 关闭进程时出错: {e}")
|
||||||
print(f" python main.py --mode=flower # 启动监控服务 (访问: {settings.flower_url})")
|
|
||||||
print("\n🌐 服务地址:")
|
print("👋 所有服务已关闭")
|
||||||
print(" API 服务: http://localhost:8000")
|
|
||||||
print(" API 文档: http://localhost:8000/docs")
|
|
||||||
print(f" 任务监控界面: {settings.flower_url}")
|
|
||||||
print("\n💡 提示:")
|
|
||||||
print(" - 请确保 Redis 和 PostgreSQL 服务已启动")
|
|
||||||
print(" - 生产环境请根据需要调整配置文件")
|
|
||||||
print(" - 建议在多个终端中分别启动不同服务")
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
def start_all_services():
|
||||||
|
"""启动所有服务"""
|
||||||
|
global processes
|
||||||
|
|
||||||
|
print("\n🚀 AI Talk Callback API 一键启动所有服务")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
# 处理启动服务请求
|
# 注册信号处理器
|
||||||
if args.mode == "api":
|
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
|
||||||
# 仅启动 FastAPI 应用
|
signal.signal(signal.SIGTERM, signal_handler) # 终止信号
|
||||||
logger.info("🚀 启动FastAPI应用...")
|
|
||||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=settings.debug)
|
try:
|
||||||
elif args.mode == "worker":
|
# 1. 启动 Celery Worker
|
||||||
# 仅启动 Celery Worker
|
print("🌿 启动 Celery Worker...")
|
||||||
logger.info("🌿 启动Celery Worker...")
|
worker_process = subprocess.Popen([
|
||||||
start_celery_worker()
|
sys.executable, "-m", "celery",
|
||||||
elif args.mode == "beat":
|
"-A", "app.celery_app",
|
||||||
# 仅启动 Celery Beat
|
"worker",
|
||||||
logger.info("📅 启动Celery Beat调度器...")
|
'--loglevel=info',
|
||||||
start_celery_beat()
|
'--pool=solo',
|
||||||
elif args.mode == "flower":
|
'--concurrency=1',
|
||||||
# 仅启动 Flower 监控服务
|
'--time-limit=300', # 5分钟任务超时
|
||||||
logger.info("📊 启动Flower监控服务...")
|
'--soft-time-limit=240' # 4分钟软超时
|
||||||
start_flower()
|
])
|
||||||
|
processes.append(worker_process)
|
||||||
|
time.sleep(2) # 等待 Worker 启动
|
||||||
|
|
||||||
|
# 2. 启动 Celery Beat
|
||||||
|
print("\n📅 启动 Celery Beat...")
|
||||||
|
beat_process = subprocess.Popen([
|
||||||
|
sys.executable, "-m", "celery",
|
||||||
|
"-A", "app.celery_app",
|
||||||
|
"beat",
|
||||||
|
'--loglevel=info',
|
||||||
|
f'--schedule={os.path.join(tempfile.gettempdir(), "celerybeat-schedule")}'
|
||||||
|
])
|
||||||
|
processes.append(beat_process)
|
||||||
|
time.sleep(2) # 等待 Beat 启动
|
||||||
|
|
||||||
|
# 3. 启动 Flower 监控(如果启用)
|
||||||
|
if settings.flower_enabled:
|
||||||
|
print("\n📊 启动 Flower 监控服务...")
|
||||||
|
flower_cmd = [
|
||||||
|
sys.executable, "-m", "celery",
|
||||||
|
"-A", "app.celery_app",
|
||||||
|
f"--broker={settings.celery_broker_url}",
|
||||||
|
"flower",
|
||||||
|
f"--port={settings.flower_port}"
|
||||||
|
]
|
||||||
|
|
||||||
|
if settings.flower_basic_auth:
|
||||||
|
flower_cmd.append(f"--basic_auth={settings.flower_basic_auth}")
|
||||||
|
if settings.flower_url_prefix:
|
||||||
|
flower_cmd.append(f"--url_prefix={settings.flower_url_prefix}")
|
||||||
|
|
||||||
|
flower_process = subprocess.Popen(flower_cmd)
|
||||||
|
processes.append(flower_process)
|
||||||
|
time.sleep(2) # 等待 Flower 启动
|
||||||
|
|
||||||
|
# 4. 启动 FastAPI 应用
|
||||||
|
print("\n🚀 启动 FastAPI 应用...")
|
||||||
|
api_cmd = [
|
||||||
|
sys.executable, "-m", "uvicorn",
|
||||||
|
"main:app",
|
||||||
|
"--host", "0.0.0.0",
|
||||||
|
"--port", "8000"
|
||||||
|
]
|
||||||
|
|
||||||
|
# 添加调试模式(如果配置了)
|
||||||
|
if settings.debug:
|
||||||
|
api_cmd.append("--reload")
|
||||||
|
|
||||||
|
api_process = subprocess.Popen(api_cmd)
|
||||||
|
processes.append(api_process)
|
||||||
|
time.sleep(2) # 等待 API 启动
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("✅ 所有服务启动完成!")
|
||||||
|
print("\n🌐 服务地址:")
|
||||||
|
print(" 🚀 API 服务: http://localhost:8000")
|
||||||
|
if settings.environment != "production" and not settings.disable_docs:
|
||||||
|
print(" 📖 API 文档: http://localhost:8000/docs")
|
||||||
|
if settings.flower_enabled:
|
||||||
|
print(f" 📊 监控界面: {settings.flower_url}")
|
||||||
|
print("\n💡 使用 Ctrl+C 可以优雅关闭所有服务")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# 等待所有进程
|
||||||
|
while True:
|
||||||
|
# 检查是否有进程异常退出
|
||||||
|
for i, process in enumerate(processes):
|
||||||
|
if process and process.poll() is not None:
|
||||||
|
print(f"❌ 进程 {i+1} 异常退出,退出码: {process.returncode}")
|
||||||
|
signal_handler(signal.SIGINT, None)
|
||||||
|
return
|
||||||
|
|
||||||
|
time.sleep(1) # 每秒检查一次
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
signal_handler(signal.SIGINT, None)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 启动服务时出错: {e}")
|
||||||
|
signal_handler(signal.SIGINT, None)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 直接启动所有服务
|
||||||
|
start_all_services()
|
||||||
|
|||||||
Reference in New Issue
Block a user