All checks were successful
Gitea Actions Demo / deploy (push) Successful in 11s
99 lines
2.9 KiB
Python
99 lines
2.9 KiB
Python
from datetime import datetime
|
||
from fastapi import FastAPI
|
||
from fastapi.concurrency import asynccontextmanager
|
||
from config.settings import settings
|
||
from scheduler import job_story_portal
|
||
from utils.logger import logger
|
||
from scheduler.scheduler import scheduler
|
||
import scheduler.jobs as jobs
|
||
from config.app import create_app
|
||
|
||
"""
|
||
启动方式:
|
||
python -m uvicorn main:app --reload
|
||
|
||
说明:
|
||
- 使用 FastAPI lifespan 管理 APScheduler 生命周期(替代已废弃的 on_event)
|
||
- 避免 Uvicorn reload 模式下调度器重复启动
|
||
- 所有 Job 在应用启动时统一注册
|
||
"""
|
||
|
||
|
||
def _add_jobs():
|
||
"""
|
||
注册所有定时任务。
|
||
|
||
注意:
|
||
- 增加重复检查,避免 reload 或多进程导致重复添加。
|
||
- replace_existing=True 保证任务更新时无需手动删除。
|
||
"""
|
||
if not scheduler.get_job("heartbeat-job"):
|
||
scheduler.add_job(
|
||
jobs.job_heartbeat,
|
||
trigger="interval",
|
||
seconds=10,
|
||
id="heartbeat-job",
|
||
replace_existing=True,
|
||
)
|
||
logger.info("Job 'heartbeat-job' registered.")
|
||
else:
|
||
logger.info("Job 'heartbeat-job' already exists. Skipped.")
|
||
|
||
if not scheduler.get_job("generate-daily-article-job"):
|
||
scheduler.add_job(
|
||
job_story_portal.job_generate_daily_article,
|
||
trigger="interval",
|
||
seconds=86400, # 每天运行一次
|
||
id="generate-daily-article-job",
|
||
replace_existing=True,
|
||
next_run_time=datetime.now(), # 启动即执行一次
|
||
)
|
||
logger.info("Job 'generate-daily-article-job' registered.")
|
||
else:
|
||
logger.info("Job 'generate-daily-article-job' already exists. Skipped.")
|
||
|
||
|
||
@asynccontextmanager
|
||
async def lifespan(app: FastAPI):
|
||
"""
|
||
FastAPI 应用生命周期管理。
|
||
|
||
startup:
|
||
- 注册定时任务
|
||
- 启动 APScheduler(避免 reload 环境下重复启动)
|
||
|
||
shutdown:
|
||
- 安全关闭 APScheduler
|
||
|
||
yield:
|
||
- 让 FastAPI 在此期间正常运行
|
||
"""
|
||
logger.info("==== Lifespan: startup ====")
|
||
|
||
# 防止 Uvicorn reload 启动两个进程导致重复启动 scheduler
|
||
if scheduler.running:
|
||
logger.warning("Scheduler already running. Skip start.")
|
||
else:
|
||
_add_jobs()
|
||
|
||
try:
|
||
scheduler.start()
|
||
logger.info("APScheduler started.")
|
||
except Exception:
|
||
logger.exception("Failed to start APScheduler:")
|
||
raise
|
||
|
||
yield # ---- 应用运行中 ----
|
||
|
||
logger.info("==== Lifespan: shutdown ====")
|
||
|
||
# 仅在 scheduler 正常运行时关闭
|
||
if scheduler.running:
|
||
scheduler.shutdown(wait=False)
|
||
logger.info("APScheduler stopped.")
|
||
else:
|
||
logger.info("Scheduler was not running. Skip shutdown.")
|
||
|
||
|
||
# 创建 FastAPI 应用(注入 lifespan 管理逻辑)
|
||
app = create_app(lifespan=lifespan) |