Skip to content

第八章:综合场景 ​

本章导读 ​

本章围绕“综合场景”展开,先交代适用场景,再说明核心原理,随后给出典型示例,最后补充小结与延伸阅读。

阅读顺序: 结论 → 原理 → 场景 → 代码 → 小结

Q21:100 个用户同时提交任务,从 BRPOP 到任务完成,完整经历了哪些步骤? ​

答:

时间 0s: 100 个 POST /creative/submit 同时到达
         100 个 task_id 被 LPUSH 到 task:queue
         Redis 列表: [100, 99, 98, ..., 2, 1]

时间 0s: Worker-1 BRPOP → 取出 1(FIFO 最早推入)
         Redis 等待链表: [W2, W3, ..., W10](9 个 Worker 继续等)
         列表: [100, 99, ..., 2]

         → 主线程执行 LPUSH 后,第1轮唤醒 W1
         → 第2轮唤醒 W2(取出 2)
         → 第3轮唤醒 W3(取出 3)
         → ...
         → 第10轮唤醒 W10(取出 10)
         → 列表: [100, 99, ..., 11](90 个任务积压)

时间 0s: W1~W10 各自处理任务 1~10
         W11~W100 在列表中等待

时间 4m: W1 完成任务 1,回到 process_queue,BRPOP → 取出 11
         W2 完成任务 2,BRPOP → 取出 12
         ...

时间 40m: 第 100 个任务被处理完成

关键:Redis 单线程在 LPUSH 后逐个唤醒 Worker,10 个 Worker 在几毫秒内全部被唤醒并拿到不同任务。瓶颈不在 Redis,而在 Worker 处理速度(每个任务 4 分钟)。


Q22:如果 Redis 服务重启了,等待链表和队列中的任务会怎样? ​

答:

全部丢失。Redis 重启后内存数据清空:

重启前:
  task:queue: [50, 49, 48, ..., 1]    ← 50 个待处理任务
  等待链表: [W1, W2, W3]               ← 3 个 Worker 在等

重启后:
  task:queue: []                       ← 空
  等待链表: []                          ← 空
  3 个 Worker 的 BRPOP 超时返回 nil

应对方案:

方案说明
AOF 持久化Redis 重启后从 AOF 文件恢复队列数据
RDB 快照定期快照,但可能丢失最近的任务
数据库兜底任务同时写入 PostgreSQL,Worker 启动时扫描数据库中 status=QUEUED 的任务重新入队

本项目的方案:任务同时存在于 PostgreSQL 和 Redis。如果 Redis 重启,可以从 PostgreSQL 恢复:

python
# Worker 启动时检查数据库中未完成的任务
def recover_tasks():
    db = SessionLocal()
    stuck_tasks = db.scalars(
        select(Task).where(Task.status.in_([
            TaskStatus.QUEUED,
            TaskStatus.SCRIPT,
            TaskStatus.IMAGE,
        ]))
    ).all()
    for task in stuck_tasks:
        redis_client.lpush("task:queue", task.id)
    db.close()

Q23:为什么本项目不用 RabbitMQ 或 Kafka,而是用 Redis BRPOP? ​

答:

维度Redis BRPOPRabbitMQKafka
部署复杂度低(已有 Redis)中(需独立部署)高(需 ZooKeeper + Broker)
原子性保证单线程串行Channel 级别锁分区单消费者
任务确认(ACK)无(需 RPOPLPUSH 弥补)内置 ACK 机制Offset 提交
延迟< 1ms~1ms~10ms
吞吐量10 万+ QPS万级 QPS百万级 QPS
适用规模中小规模(< 200 Worker)中大规模超大规模

选择 Redis 的原因:

  1. 项目已经依赖 Redis(进度缓存、审核队列),无需额外部署
  2. MVP 阶段任务量不大(每天几十到几百个任务),Redis 足够
  3. 部署简单,运维成本低
  4. BRPOP 延迟极低(< 1ms),满足实时性要求

何时考虑迁移:

  • Worker 超过 200 个,Redis 单线程开始成为瓶颈
  • 需要复杂的任务路由、重试、死信队列
  • 需要消息持久化保证(Redis 重启丢数据风险)