from fastapi import FastAPI from settings import TORTOISE_ORM from fastapi.middleware.cors import CORSMiddleware from tortoise.contrib.fastapi import register_tortoise from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.interval import IntervalTrigger from tortoise import Tortoise from contextlib import asynccontextmanager from apis import app as main_router import asyncio import signal import sys @asynccontextmanager async def lifespan(app: FastAPI): """ 应用生命周期管理函数 - 启动:注册定时任务并启动调度器 - 关闭:优雅关闭调度器与数据库连接 """ print('项目启动...') # 初始化数据库连接(使用 Tortoise 直接初始化,确保路由与定时任务可用) try: await Tortoise.init(config=TORTOISE_ORM) print('数据库初始化完成') except Exception as e: print(f'数据库初始化失败: {e}') # 每30分钟保持一次数据库连接活跃 scheduler.add_job( keep_db_connection_alive, IntervalTrigger(minutes=30), id='keep_db_alive', name='保持数据库连接', coalesce=True, misfire_grace_time=30, ) scheduler.start() try: yield finally: print('项目结束...') # 关闭数据库连接 print('关闭数据库连接...') try: await asyncio.wait_for(Tortoise.close_connections(), timeout=2) except asyncio.TimeoutError: print('关闭数据库连接超时') except Exception as e: print(f'关闭数据库连接出错: {e}') # 关闭调度器 print('关闭调度器...') try: if scheduler is not None and hasattr(scheduler, 'shutdown'): scheduler.shutdown(wait=False) except Exception as e: print(f'关闭调度器出错: {e}') # 创建 FastAPI 应用实例 app = FastAPI(lifespan=lifespan) # 配置 CORS 中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 创建调度器实例 scheduler = AsyncIOScheduler() # 包含主路由 app.include_router(main_router) # 注意:使用自定义 lifespan 已在启动时手动初始化数据库。 # 若改回默认事件机制,可重新启用 register_tortoise。 async def keep_db_connection_alive(): """ 保持数据库连接活跃的函数 定期执行简单查询以防止连接超时 """ try: conn = Tortoise.get_connection("default") await conn.execute_query("SELECT 1") print("数据库连接检查成功") except Exception as e: print(f"数据库连接检查失败: {e}") def signal_handler(): """ 处理终止信号,确保资源正确释放 """ async def shutdown(): print("收到终止信号,开始优雅关闭...") # 关闭数据库连接 print("关闭数据库连接...") try: await Tortoise.close_connections() except Exception as e: print(f"关闭数据库连接时出错: {e}") # 关闭调度器 print("关闭调度器...") try: scheduler.shutdown() except Exception as e: print(f"关闭调度器时出错: {e}") print("所有资源已关闭,程序退出") sys.exit(0) loop = asyncio.get_event_loop() loop.create_task(shutdown()) # 给异步任务一些时间完成 loop.run_until_complete(asyncio.sleep(2)) sys.exit(0) if __name__ == '__main__': from uvicorn import run # 注册信号处理 for sig in (signal.SIGINT, signal.SIGTERM): signal.signal(sig, lambda sig, frame: signal_handler()) run( 'main:app', host='0.0.0.0', port=6060, reload=False, workers=1, # loop='uvloop', http='httptools', limit_concurrency=10000, backlog=4096, timeout_keep_alive=5 )