4. 基于 List 的请求队列:LPUSH + BRPOP 实现先进先出队列与生产者消费者模型

在削峰填谷场景中,请求队列是最基础、最直接的缓冲手段。Redis 的 List 数据结构天然支持双向操作,通过 LPUSH 与 BRPOP 的组合,可以快速搭建一个具备阻塞读取能力的先进先出(FIFO)队列,实现经典的生产者-消费者模型。

4.1 核心命令与队列语义

命令 角色 行为描述 时间复杂度
LPUSH key value [value ...] 生产者 将一个或多个元素从 List 头部插入 O(1) 每个元素
RPOP key 消费者 从 List 尾部取出一个元素(非阻塞) O(1)
BRPOP key [key ...] timeout 消费者 从 List 尾部阻塞弹出,超时返回 nil O(1)

使用 LPUSH 写入、BRPOP 读取,形成 左进右出 的 FIFO 顺序。消费者在队列为空时自动挂起等待,避免空轮询消耗 CPU。

4.2 生产者-消费者模型架构

LPUSH + BRPOP 生产者-消费者模型 生产者 1 生产者 2 生产者 N Redis List 队列 queue:requests [请求A] → [请求B] → [请求C] LPUSH 头部写入 BRPOP 尾部阻塞弹出 FIFO 顺序 消费者 1 消费者 2 消费者 M 多个生产者并发 LPUSH,多个消费者并发 BRPOP 争抢任务,天然实现负载均衡 消费者在队列为空时阻塞等待,不消耗 CPU

4.3 代码实现:生产者与消费者

4.3.1 生产者:请求入队

# Python 示例:生产者将请求 ID 推入队列
import redis
import json

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def produce_request(request_id, payload):
    message = json.dumps({
        'id': request_id,
        'data': payload,
        'timestamp': int(time.time())
    })
    # LPUSH 从头部插入
    r.lpush('queue:requests', message)
    return True

# 模拟高并发写入
for i in range(1000):
    produce_request(f'req_{i}', {'user': f'user_{i}'})

4.3.2 消费者:阻塞读取并处理

# Python 示例:消费者使用 BRPOP 阻塞获取任务
import redis
import json

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def consume_loop():
    while True:
        # BRPOP 阻塞弹出,超时 0 表示永久等待
        result = r.brpop('queue:requests', timeout=0)
        # result 返回 (key, value) 元组
        key, message = result
        request = json.loads(message)
        process_request(request)

def process_request(request):
    # 模拟业务处理
    print(f"处理请求 {request['id']},用户 {request['data']['user']}")
    # 实际业务逻辑:数据库写入、调用下游服务等
关键参数说明:
BRPOP 的 timeout 参数单位是秒,设置为 0 表示无限阻塞,直到有新元素可用。生产环境中建议设置一个合理的超时时间(如 30 秒),以便消费者定期检查连接健康状态。

4.4 削峰效果与性能指标

指标 无队列直接处理 LPUSH + BRPOP 队列 提升幅度
瞬时峰值处理能力 受限于下游吞吐 队列无限缓冲,峰值被削平 10x ~ 100x
消费者 CPU 空转 轮询消耗高 阻塞等待,零空转 降低 90%+
请求丢失率 高(直接拒绝) 低(队列持久化) 显著降低
单机 QPS 上限 约 5,000 50,000+ 10x

4.5 生产环境注意事项

  • 队列长度监控:使用 LLEN queue:requests 实时监控积压量,设置告警阈值,防止内存溢出。
  • 消费者数量规划:消费者数量建议等于下游处理能力的 1.5~2 倍,避免消费者过多导致 Redis 连接数爆炸。
  • 消息幂等性:消费者处理失败时,建议将消息重新入队或写入死信队列,避免重复消费导致数据不一致。
  • 连接管理:每个消费者使用独立的 Redis 连接,避免连接复用导致的阻塞干扰。
避坑指南:
不要使用 RPOP 替代 BRPOP 做消费者读取。非阻塞模式下,空队列会导致消费者陷入高频轮询,CPU 飙升且 Redis QPS 被无意义消耗。始终使用阻塞版本 BRPOP 或 BLPOP。

4.6 适用场景与边界

适用场景 不适用场景
请求削峰:秒杀、抢票、预约 需要消息可靠投递(无 ACK 机制)
异步任务分发:邮件、短信、推送 需要消息回溯或重放
日志收集与缓冲 需要严格顺序保证(单消费者可保证)
简单生产者-消费者解耦 需要广播/发布订阅模式

基于 List 的请求队列是 Redis 削峰体系中最轻量、最易上手的方案。它没有复杂的配置,不需要额外的中间件,适合作为业务削峰的第一道防线。当业务对消息可靠性、延迟、顺序有更高要求时,可以在此基础上叠加确认机制或迁移至专业消息队列。