主题
四、阻塞机制详解
4.1 阻塞 vs 轮询
轮询模式(RPOP):
客户端: 有没有?Redis: 没有。
客户端: 有没有?Redis: 没有。
客户端: 有没有?Redis: 没有。
客户端: 有没有?Redis: 有!(第 0.3 秒)
客户端: sleep(0.1)...(继续下一轮)
→ CPU 一直在跑,延迟 0.1 秒
阻塞模式(BRPOP):
客户端: 有没有?我等 5 秒。Redis: ...
(客户端挂起,不占 CPU)
(3 秒后有人 LPUSH)
Redis: 有!
客户端: 立即处理
→ CPU 零占用,延迟 < 1 毫秒4.2 Redis 服务端如何实现阻塞
Redis 为每个阻塞命令维护一个等待链表:
key: task:queue
waiting_clients: [Client-A, Client-B, Client-C]
(按 BRPOP 调用时间排队)当 LPUSH task:queue 6 执行时:
1. Redis 执行 LPUSH,列表变为 [6]
2. Redis 检查 waiting_clients,发现 Client-A 在等
3. Redis 直接把 "6" 发给 Client-A(不从列表弹出)
4. 从列表删除 "6",列表变为 []
5. 从 waiting_clients 移除 Client-A关键:数据不经过列表中转,直接从 LPUSH 方传递给 BRPOP 方,O(1) 时间完成。
4.3 超时行为
python
# timeout=5:最多等 5 秒
result = redis_client.brpop("task:queue", timeout=5)
# 5 秒内没来新元素 → 返回 None
# timeout=0:永久阻塞
result = redis_client.brpop("task:queue", timeout=0)
# 永远等到有元素才返回(不推荐,无法响应 Ctrl+C)本项目为什么用 timeout=5:
python
while True:
result = redis_client.brpop("task:queue", timeout=5)
if result:
run_task(...)
# ← 每 5 秒回到循环顶部,检查 KeyboardInterrupt- timeout=0 永久阻塞 →
Ctrl+C无法中断 - timeout=5 → 每 5 秒回到
while True顶部,能响应Ctrl+C - 5 秒是经验值,太短浪费 CPU(频繁循环),太长退出延迟
4.4 多 Worker 竞争
多个 Worker 同时 BRPOP 同一个队列时,Redis 保证只有一个 Worker 能拿到元素:
Worker-1: BRPOP task:queue 5 ──┐
Worker-2: BRPOP task:queue 5 ──┤ 三个 Worker 都在等
Worker-3: BRPOP task:queue 5 ──┘
API: LPUSH task:queue 6
Redis: 只有 Worker-1 拿到 "6"
Worker-2、Worker-3 继续等待等待顺序:先调 BRPOP 的 Worker 优先拿到(FIFO 等待链表)。