Redis Pub/Sub 不支持负载均衡,因其是纯广播模型,所有订阅者均收到全部消息;任务分发应使用 List + BRPOP 实现原子出队与多消费者公平竞争。

Redis Pub/Sub 本身不提供负载均衡能力
Pub/Sub 是纯广播模型:所有订阅了同一频道的客户端,都会收到每一条消息。它没有内置的“轮询分发”“竞争消费”或“ACK 确认”机制,因此不能直接用于任务分发场景下的负载均衡。如果你看到多个消费者同时收到同一条任务,这不是负载均衡,是重复执行——通常会导致数据错乱或资源浪费。
为什么 PUBSUB 命令和 SUBSCRIBE 无法实现任务分发
常见误解是用 SUBSCRIBE 启动多个 worker,以为 Redis 会自动挑一个投递。但事实是:
- 每个
SUBSCRIBE连接都独立接收全量消息 -
PUBSUB NUMSUB只返回订阅者数量,不参与路由决策 - 断连时消息直接丢失(Pub/Sub 无持久化)
- 没有消息重试、超时、失败回退等任务队列必需语义
真正可行的替代方案:用 List + BRPOP 或 BLPOP
要实现“一个任务只被一个消费者取走”,必须依赖原子出队操作。Redis 的阻塞列表是最轻量且广泛验证的方案:
生产者用:LPUSH task_queue {"job":"send_email","to":"a@b.com"}
消费者用:BRPOP task_queue 0(阻塞等待,0 表示永不超时)
关键点:
-
BRPOP是原子操作:多个客户端并发执行时,Redis 保证只有一个能拿到元素 - 天然支持多消费者公平竞争,效果就是负载均衡
- 配合
TIMEOUT参数可控制空闲等待时长,避免长连接僵死 - 若需失败重入队,得靠业务层手动
LPUSH,Redis 不管重试逻辑
如果坚持用 Pub/Sub,必须自己补足缺失环节
极少数场景(如实时通知广播)需要 Pub/Sub 的低延迟特性,又想模拟“随机选一个消费者干活”,只能在客户端做协调:
- 所有 worker 订阅同一频道,收到消息后先向 Redis 写一个带过期时间的锁:
SETNX worker_lock "worker_123" EX 5 - 只有写入成功的 worker 才真正执行任务
- 其他 worker 检测到锁已存在,就丢弃该消息
- 注意时钟漂移、网络分区可能导致锁失效,实际稳定性远低于
BRPOP
这个模式绕开了 Redis 的设计初衷,增加了复杂度和故障面,不推荐作为任务分发主路径。
真正容易被忽略的是:Pub/Sub 的吞吐优势只在“一对多广播”时成立;一旦你开始加锁、去重、重试,它的简洁性就没了,而可靠性还没达到专业队列水平。


















