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

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 个模型到缓存

根本原因

容器启动顺序问题 + 内存缓存架构缺陷

  1. 使用进程级内存缓存,每个 Celery Worker 独立缓存
  2. Worker 启动时数据库可能还没有模型数据
  3. 多个 Worker 重复缓存,浪费内存
  4. 缓存更新需要重启所有 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.py
  • server/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 - 异步调用

验证步骤

  1. 重启容器:

    cd server
    docker-compose restart app celery-worker-ai celery-worker-export
    
  2. 检查 FastAPI 启动日志:

    docker logs jointo-server-app | grep "AI Provider Factory"
    

    应该看到:

    ✅ AI Provider Factory initialized (Redis cache)
    
  3. 检查 Celery Worker 日志:

    docker logs jointo-server-celery-ai | grep "AI Provider Factory"
    

    应该看到:

    ✅ AI Provider Factory 缓存已从 Redis 加载(Celery Worker)
       已加载 21 个模型到缓存
    
  4. 验证 Redis 缓存:

    docker exec jointo-server-redis redis-cli GET "ai_models:provider_cache"
    
  5. 测试 AI 任务执行

影响

正面影响

  • 解决了 Worker 启动时缓存为空的问题
  • 所有 Worker 共享缓存,节省内存
  • 支持缓存热更新,无需重启
  • 降低数据库查询压力

注意事项

  • ⚠️ 依赖 Redis 服务,Redis 不可用时任务会失败
  • ⚠️ 缓存有 1 小时过期时间,过期后会自动重新加载
  • ⚠️ 需要确保 FastAPI 应用先于 Celery Worker 启动

后续优化建议

  1. 实现缓存预热机制(应用启动前确保缓存已就绪)
  2. 添加缓存监控和告警
  3. 实现缓存降级策略(Redis 不可用时回退到数据库查询)
  4. 考虑使用 Redis Sentinel 或 Cluster 提高可用性