保定ai问答主体项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
bd_ai_fastapi/app/__init__.py

142 lines
4.3 KiB

import os
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
from tortoise import Tortoise
from redis.asyncio import Redis
from fastapi.staticfiles import StaticFiles
from aiomysql import create_pool, OperationalError as AiomysqlOperationalError
from app.core.exceptions import SettingNotFound
from app.core.init_app import (
init_data,
make_middlewares,
register_exceptions,
register_routers,
)
try:
from app.settings.config import settings
except ImportError:
raise SettingNotFound("Can not import settings")
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
# 初始化资源
app.state.mysql_pool = None
app.state.redis_client = None
try:
# 初始化 MySQL 连接池
app.state.mysql_pool = await create_pool(
host=settings.FLOW_MYSQL_HOST,
port=settings.FLOW_MYSQL_PORT,
user=settings.FLOW_MYSQL_USER,
password=settings.FLOW_MYSQL_PASSWORD,
db=settings.FLOW_MYSQL_DB,
# 核心并发参数
minsize=15, # 初始保持20个连接
maxsize=50, # 最大连接数
# 连接管理优化
pool_recycle=100, # 180秒回收连接
connect_timeout=15, # 连接超时时间
# 其他优化
charset='utf8mb4',
echo=False,
)
# 验证连接有效性
async with app.state.mysql_pool.acquire() as conn:
await conn.ping()
logger.info("✅ MySQL 连接池初始化成功")
except AiomysqlOperationalError as e:
logger.error(f"❌ MySQL 连接池初始化失败: 数据库操作错误 - {str(e)}")
raise
except Exception as e:
logger.error(f"❌ MySQL 连接池初始化失败: 未知错误 - {str(e)}")
raise
try:
# 初始化 Redis 连接
app.state.redis_client = Redis(
host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
db=settings.REDIS_DB,
decode_responses=True,
password=settings.REDIS_PASSWORD,
socket_connect_timeout=5,
socket_keepalive=True,
retry_on_timeout=True,
# 连接池参数
max_connections=1024, # Redis连接池最大连接数
health_check_interval=30,
retry_on_error=[ConnectionError, TimeoutError], # 重试错误类型
single_connection_client=False, # 不使用单连接客户端
auto_close_connection_pool=True # 自动关闭连接池
)
await app.state.redis_client.ping()
logger.info("✅ Redis 连接成功")
except Exception as e:
logger.error(f"❌ Redis 连接失败: {str(e)}")
try:
await init_data()
except Exception as e:
logger.error(f"❌ 初始化数据失败: {str(e)}")
raise
yield
# 资源清理
if app.state.redis_client:
try:
await app.state.redis_client.close()
logger.info("🛑 Redis 连接已关闭")
except Exception as e:
logger.warning(f" Redis 关闭失败: {str(e)}")
if app.state.mysql_pool:
try:
app.state.mysql_pool.close()
await app.state.mysql_pool.wait_closed()
logger.info("🛑 MySQL 连接池已关闭")
except Exception as e:
logger.warning(f" MySQL 连接池关闭失败: {str(e)}")
try:
await Tortoise.close_connections()
logger.info("🛑 Tortoise 连接已关闭")
except Exception as e:
logger.warning(f" Tortoise 连接关闭失败: {str(e)}")
def create_app() -> FastAPI:
app = FastAPI(
title=settings.APP_TITLE,
description=settings.APP_DESCRIPTION,
version=settings.VERSION,
openapi_url="/openapi.json",
middleware=make_middlewares(),
lifespan=lifespan,
)
static_dir = os.path.join(settings.BASE_DIR, 'web', 'public', 'resource')
if os.path.exists(static_dir):
app.mount("/resource", StaticFiles(directory=static_dir), name="resource")
else:
logger.warning(f" 静态文件目录不存在: {static_dir}")
register_exceptions(app)
register_routers(app, prefix="/api")
return app
app = create_app()