← 中文资源

简体中文版本

为请求增加优先级队列

用独立队列、按权重分配的 worker 和老化机制,让交互请求排在批处理之前并保持延迟目标。

阅读英文原文

概述

优先级队列只决定下一个执行哪个任务,并不增加处理能力。如果所有 worker 都在跑长耗时导出,密码重置邮件仍然要等待。正确做法是把排序与保留容量结合:多数 worker 按高优先级优先消费两个队列,少数 worker 只消费低优先级队列,保证批处理持续前进,同时压低交互延迟。

步骤

  1. 使用两个 Redis 列表,并用 Lua 脚本原子地从高优先级队列取任务,取不到再取低优先级:
local job = redis.call('LPOP', KEYS[1])
if job then return {1, job} end
job = redis.call('LPOP', KEYS[2])
if job then return {2, job} end
return {0, ''}
  1. 按 8:2 分配 worker。前 8 个消费交互和批处理队列,后 2 个只消费批处理队列,避免低优先级完全饿死。
const queues = i < 8 ? ['jobs:interactive', 'jobs:batch'] : ['jobs:batch'];
startWorker(queues);
  1. 为每个优先级记录等待时间直方图,批处理最老任务超过 15 分钟触发告警。
  2. 压测验证:先入队 10000 个批处理任务,再入队 1 个交互任务。

验证

预期交互任务在 1 秒内完成,不受 10000 个批处理任务影响。仪表盘上应能分别看到两个优先级的等待时间;若低优先级等待持续上升而高优先级正常,说明保留容量不足。还要测试 worker 崩溃:使用 BRPOPLPUSH 或可见性超时,确保任务不会在取出后丢失。

注意事项

  • 严格优先级会导致低优先级饥饿,必须保留 worker 或实现老化提升。
  • 队列积压过快时应在上游做准入控制,而不是只调整优先级。
  • 多个 worker 并发取任务时务必保证原子性,两次独立 LPOP 存在竞态。
  • 交互任务本身也可能超时,需要单独的执行超时与重试策略。