feat(startup): 服务端启动 bootstrap 与缓存预热
- 新增 STARTUP_BOOTSTRAP_ENABLED / STARTUP_CACHE_WARMUP_ENABLED 配置开关 - lifespan 拆分 bootstrap 步骤并后台线程预热缓存,失败可降级继续启动 - server_start.sh / web_start.sh 扩展 SERVER_PORT、启动与调度开关的 env 覆盖 - bootstrap_paddleocr_mobile.sh 改用 python3 并补充 poppler-utils 依赖 - 补充启动 bootstrap 与 env 覆盖优先级测试
This commit is contained in:
@@ -43,6 +43,8 @@ class Settings(BaseSettings):
|
||||
app_port: int = Field(default=8000, alias="SERVER_PORT")
|
||||
server_workers: int = Field(default=1, alias="SERVER_WORKERS")
|
||||
web_concurrency: int | None = Field(default=None, alias="WEB_CONCURRENCY")
|
||||
startup_bootstrap_enabled: bool = Field(default=True, alias="STARTUP_BOOTSTRAP_ENABLED")
|
||||
startup_cache_warmup_enabled: bool = Field(default=False, alias="STARTUP_CACHE_WARMUP_ENABLED")
|
||||
background_schedulers_enabled: bool = Field(
|
||||
default=True,
|
||||
alias="BACKGROUND_SCHEDULERS_ENABLED",
|
||||
|
||||
@@ -2,6 +2,8 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
from logging import Logger
|
||||
import threading
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -10,11 +12,13 @@ from app.api.router import api_router
|
||||
from app.core.config import get_settings
|
||||
from app.core.logging import get_logger, setup_logging
|
||||
from app.core.openapi import API_DESCRIPTION, OPENAPI_TAGS
|
||||
from app.db.session import get_session_factory
|
||||
from app.middleware.logging import AccessLogMiddleware
|
||||
from app.schemas.common import RootStatusRead
|
||||
from app.services.agent_foundation import prepare_agent_foundation
|
||||
from app.services.digital_employee_reminder_scheduler import digital_employee_reminder_scheduler
|
||||
from app.services.employee import prepare_employee_directory
|
||||
from app.services.employee import EmployeeService
|
||||
from app.services.employee_profile_scheduler import employee_profile_scheduler
|
||||
from app.services.finance_dashboard_scheduler import finance_dashboard_scheduler
|
||||
from app.services.finance_report_scheduler import finance_report_scheduler
|
||||
@@ -23,6 +27,8 @@ from app.services.knowledge import prepare_knowledge_library
|
||||
from app.services.knowledge_index_tasks import knowledge_index_task_manager
|
||||
from app.services.knowledge_rag import shutdown_knowledge_rag_runtime
|
||||
from app.services.knowledge_scheduler import knowledge_index_scheduler
|
||||
from app.services.settings import SettingsService
|
||||
from app.services.user_session_metrics import UserSessionMetricService
|
||||
|
||||
|
||||
def _effective_server_workers(settings: object) -> int:
|
||||
@@ -42,15 +48,55 @@ def _should_start_background_schedulers(settings: object) -> bool:
|
||||
return _effective_server_workers(settings) <= 1
|
||||
|
||||
|
||||
def _run_startup_bootstrap(logger: Logger) -> None:
|
||||
steps = (
|
||||
("employee_directory", prepare_employee_directory),
|
||||
("agent_foundation", prepare_agent_foundation),
|
||||
("knowledge_library", prepare_knowledge_library),
|
||||
("hermes_skills", sync_repository_hermes_skills),
|
||||
)
|
||||
for name, step in steps:
|
||||
try:
|
||||
step()
|
||||
except Exception:
|
||||
logger.exception("Startup bootstrap step failed; continuing degraded name=%s", name)
|
||||
|
||||
|
||||
def _warm_startup_caches(logger: Logger) -> None:
|
||||
try:
|
||||
session_factory = get_session_factory()
|
||||
with session_factory() as db:
|
||||
SettingsService(db).ensure_settings_ready()
|
||||
EmployeeService(db).ensure_directory_ready()
|
||||
UserSessionMetricService(db).ensure_storage_ready()
|
||||
logger.info("Startup cache warmup complete")
|
||||
except Exception:
|
||||
logger.exception("Startup cache warmup failed; continuing without warm cache")
|
||||
|
||||
|
||||
def _start_cache_warmup_thread(logger: Logger) -> None:
|
||||
thread = threading.Thread(
|
||||
target=_warm_startup_caches,
|
||||
args=(logger,),
|
||||
name="x-financial-startup-cache-warmup",
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
||||
settings = get_settings()
|
||||
logger = get_logger("app.main")
|
||||
|
||||
prepare_employee_directory()
|
||||
prepare_agent_foundation()
|
||||
prepare_knowledge_library()
|
||||
sync_repository_hermes_skills()
|
||||
if settings.startup_bootstrap_enabled:
|
||||
_run_startup_bootstrap(logger)
|
||||
else:
|
||||
logger.warning("Startup bootstrap skipped because STARTUP_BOOTSTRAP_ENABLED=false")
|
||||
|
||||
if settings.startup_cache_warmup_enabled:
|
||||
_start_cache_warmup_thread(logger)
|
||||
|
||||
schedulers_started = _should_start_background_schedulers(settings)
|
||||
if schedulers_started:
|
||||
knowledge_index_scheduler.start()
|
||||
|
||||
Reference in New Issue
Block a user