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.
5.6 KiB
5.6 KiB
Celery Worker AI 模型缓存初始化失败修复
日期: 2026-02-11
类型: Bug 修复 + 架构优化
影响范围: Celery Worker (AI 任务队列)、AI Provider Factory
问题描述
Celery Worker 启动时 AI Provider Factory 缓存初始化失败,导致所有 AI 任务执行时报错:
模型 gpt-4o-mini 不存在或未激活。请检查:
1) 模型是否已同步到数据库
2) 模型是否已启用(is_active=True)
日志显示:
AI Provider Factory 初始化完成: 已加载 0 个模型到缓存
根本原因
容器启动顺序问题 + 内存缓存架构缺陷:
- ❌ 使用进程级内存缓存,每个 Celery Worker 独立缓存
- ❌ Worker 启动时数据库可能还没有模型数据
- ❌ 多个 Worker 重复缓存,浪费内存
- ❌ 缓存更新需要重启所有 Worker
解决方案
架构升级:从内存缓存迁移到 Redis 缓存
新架构:
FastAPI App 启动 → 初始化 Redis 缓存(从数据库加载模型)
↓
Celery Worker 启动 → 从 Redis 读取缓存(无需查询数据库)
↓
任务执行 → 使用 Redis 缓存的模型信息
优势:
- ✅ 所有 Worker 共享同一份缓存
- ✅ Worker 启动时如果 Redis 有缓存,直接使用
- ✅ 缓存持久化,重启不丢失
- ✅ 支持热更新(刷新缓存无需重启)
- ✅ 降低数据库查询压力
1. 整合 Redis 连接管理 (app/core/cache.py)
在现有的 CacheService 基础上,添加便捷函数:
async def get_redis() -> redis.Redis:
"""获取 Redis 客户端实例(单例模式)"""
if cache.client is None:
await cache.connect()
return cache.client
async def close_redis():
"""关闭 Redis 连接"""
await cache.close()
2. 重构 AI Provider Factory (app/services/ai_providers/factory.py)
改动:
- 移除内存缓存
_provider_cache: Dict[str, int] - 使用 Redis 缓存:
CACHE_KEY = "ai_models:provider_cache" - 缓存过期时间:
CACHE_TTL = 3600(1 小时) - 所有方法改为异步:
async def create_provider(...)
缓存结构:
{
"gpt-4o-mini": 1,
"gemini-2.5-flash": 3,
"dall-e-3": 1,
...
}
3. 修改 FastAPI 启动逻辑 (app/main.py)
在应用启动时初始化 Redis 缓存:
async def lifespan(app: FastAPI):
# 启动
await init_db()
# 初始化 AI Provider Factory 缓存(Redis)
async for db in get_session():
await AIProviderFactory.initialize(db)
break
yield
# 关闭 Redis 连接
await close_redis()
4. 优化 Celery Worker 初始化 (app/core/celery_app.py)
改进:
- 优先从 Redis 读取缓存
- 如果 Redis 缓存不存在,从数据库初始化
- 增加重试次数:3 → 5 次
- 增加重试延迟:2 → 3 秒
- 更详细的错误诊断信息
@worker_ready.connect
def initialize_worker(**kwargs):
# 1. 尝试从 Redis 读取缓存
cached_data = await redis.get(AIProviderFactory.CACHE_KEY)
if cached_data:
logger.info("✅ 从 Redis 加载缓存")
return
# 2. Redis 缓存不存在,从数据库初始化
await AIProviderFactory.initialize(db)
5. 更新所有调用点
将同步调用改为异步:
# ❌ 旧代码
provider = AIProviderFactory.create_provider(model)
# ✅ 新代码
provider = await AIProviderFactory.create_provider(model)
涉及文件:
server/app/services/ai_service.pyserver/app/tasks/ai_tasks.py(7 处)server/scripts/dev/test_aihubmix_provider.py
修改文件
- ✅ 删除:
server/app/core/redis.py- 重复文件 - ✅ 修改:
server/app/core/cache.py- 添加get_redis()和close_redis()函数 - ✅ 修改:
server/app/services/ai_providers/factory.py- 改用 Redis 缓存 - ✅ 修改:
server/app/main.py- 启动时初始化 Redis 缓存 - ✅ 修改:
server/app/core/celery_app.py- 优化初始化逻辑 - ✅ 修改:
server/app/services/ai_service.py- 异步调用 - ✅ 修改:
server/app/tasks/ai_tasks.py- 异步调用(已完成) - ✅ 修改:
server/scripts/dev/test_aihubmix_provider.py- 异步调用
验证步骤
-
重启容器:
cd server docker-compose restart app celery-worker-ai celery-worker-export -
检查 FastAPI 启动日志:
docker logs jointo-server-app | grep "AI Provider Factory"应该看到:
✅ AI Provider Factory initialized (Redis cache) -
检查 Celery Worker 日志:
docker logs jointo-server-celery-ai | grep "AI Provider Factory"应该看到:
✅ AI Provider Factory 缓存已从 Redis 加载(Celery Worker) 已加载 21 个模型到缓存 -
验证 Redis 缓存:
docker exec jointo-server-redis redis-cli GET "ai_models:provider_cache" -
测试 AI 任务执行
影响
正面影响:
- ✅ 解决了 Worker 启动时缓存为空的问题
- ✅ 所有 Worker 共享缓存,节省内存
- ✅ 支持缓存热更新,无需重启
- ✅ 降低数据库查询压力
注意事项:
- ⚠️ 依赖 Redis 服务,Redis 不可用时任务会失败
- ⚠️ 缓存有 1 小时过期时间,过期后会自动重新加载
- ⚠️ 需要确保 FastAPI 应用先于 Celery Worker 启动
后续优化建议
- 实现缓存预热机制(应用启动前确保缓存已就绪)
- 添加缓存监控和告警
- 实现缓存降级策略(Redis 不可用时回退到数据库查询)
- 考虑使用 Redis Sentinel 或 Cluster 提高可用性