Redis队列应选用lpush+brpop组合,禁用lpop以防轮询和丢任务;brpop需设timeout(推荐3秒)并捕获ConnectionError/TimeoutError;序列化统一用json.dumps(ensure_ascii=False),禁用pickle;Web框架中发任务须用连接池和异步调用;worker需实现“取出暂存→处理→确认删除”机制保障不丢任务。

Redis队列选 lpush + brpop,别用 lpop
同步阻塞读取必须用 brpop,否则消费者会疯狂轮询、CPU拉满还丢任务。Python里用 redis-py 的 brpop 默认阻塞,超时设为 0 就一直等,设成 1~5 秒更稳妥——避免主进程被卡死又不漏活。
常见错误现象:lpop 返回 None 后立刻重试,日志刷屏、Redis连接数暴涨;或者没加异常捕获,网络抖动时整个 worker 进程退出。
-
brpop第二个参数是 timeout,建议显式写成brpop("queue_name", timeout=3),别依赖默认值 - 生产环境务必包装 try/except,捕获
ConnectionError和TimeoutError - 不要在一个连接上混用
brpop和管道(pipeline),阻塞命令不能进 pipeline
任务序列化用 json.dumps,别用 pickle
跨语言或未来可能换 worker 语言时,pickle 是定时炸弹:反序列化任意代码执行风险高,且 Python 版本不兼容就直接报 UnicodeDecodeError 或 AttributeError。
使用场景很明确:只要任务数据是 dict/list/str/int/bool/None 组合,json.dumps 安全、轻量、可读性强。
立即学习“Python免费学习笔记(深入)”;
- 中文要加
ensure_ascii=False,否则全是\u4f60\u597d,查问题反人类 - 时间字段统一转成 ISO 格式字符串(
datetime.isoformat()),别传datetime对象 - 如果必须传 bytes(比如小图 base64),先
base64.b64encode(...).decode()再 json 化
Django/Flask 中发任务别在请求线程里直连 Redis
Web 框架主线程是宝贵的,Redis 网络延迟一抖,整个 HTTP 响应就卡住。哪怕只是 r.lpush("task_queue", payload),也要确认它走的是短连接池、有超时、失败能降级。
性能影响明显:本地 Redis 延迟通常 0.2~1ms,但容器间或跨 AZ 可能飙到 20ms+,QPS 高时直接拖垮响应 P95。
- 用连接池:
redis.ConnectionPool(max_connections=20, socket_timeout=0.5, socket_connect_timeout=0.5) - 发任务加
try/except redis.ConnectionError,失败时记日志+返回用户“稍后重试”,别让请求崩掉 - 异步框架(如 FastAPI +
anyio)里,至少用await r.lpush(...),别用同步 client
worker 进程挂了,任务不能丢
Redis 列表本身不保证消息不丢,brpop 取出即删。worker 处理中崩溃,任务就彻底消失——这是最常被忽略的点。
解决思路不是“全量持久化”,而是“取出后暂存、处理完再删”:用 r.lmove(Redis 6.2+)或两阶段操作(r.rpoplpush)把任务挪到 processing 队列,成功后再 r.lrem 或直接 r.lpop。
- 低版本 Redis 用
r.rpoplpush("main_queue", "processing_queue"),配合 TTL 设置 processing 队列过期时间(比如 300 秒) - worker 启动时扫描
processing_queue,把超时任务移回 main 或发告警 - 别依赖
ACK机制幻想“自动重投”,Redis 没内置 ACK,得自己实现状态机
真正难的不是怎么推任务,是怎么确保每条都“至少处理一次”。超时判定、重复幂等、死信归档——这些细节堆起来,才决定队列到底靠不靠谱。










