redis-benchmark无法真实压测pub/sub,因其单连接循环模式不触发client-output-buffer-limit溢出;应使用多线程python脚本模拟30+独立订阅连接,结合时间戳测延迟、sorted set统计丢包,并注意docker网络栈对延迟的影响。

用多线程 Python 模拟真实订阅压力,别信 redis-benchmark
redis-benchmark -t pubsub 压不出真实延迟和丢包,它只建 1 个发布连接 + 1 个订阅连接,反复循环发收,不触发 client-output-buffer-limit 溢出,也不模拟多客户端竞争缓冲区。线上一开 30+ 个订阅线程,Redis 就开始断连、丢消息——这压测根本测不出来。
正确做法是写一个 Python 脚本,用 threading 启动 N 个独立 redis.Redis().pubsub() 实例,每个都 SUBSCRIBE 同一频道,再由另一个线程高频 PUBLISH 带递增序号的消息(如 "msg:12345")。关键点:
- 每个线程必须调用
pubsub.listen()进入阻塞读,不能用get_message(block=False)轮询,否则无法复现 TCP 长连接下的缓冲区堆积 - 发布端要记录每条消息的发送时间戳(
time.time_ns()),订阅端收到后比对,算单跳延迟 - 用集合或 Redis Sorted Set 在服务端暂存已发序号,订阅端上报收到的序号,最后比对差集得丢失率
检查 client-output-buffer-limit 是否被击穿
Redis 默认配置 client-output-buffer-limit pubsub 32mb 8mb 60,意思是:当某个订阅连接的输出缓冲区超过 32MB,或连续 60 秒内超过 8MB,就强制断开该连接。高并发下极易触发,表现为客户端日志出现 Connection closed by server 或 Redis 日志打印 Client output buffer too big。
实操建议:
- 在测试前先用
CONFIG GET client-output-buffer-limit确认当前值,必要时临时调大(如128mb 32mb 120),但注意内存水位 - 监控
redis_connected_clients指标是否突降,配合redis_rejected_connections_total上升,基本可锁定是缓冲区溢出导致的批量断连 - 不要只看 INFO PUBSUB 返回的
channels和patterns数,它们不反映连接健康状态
Docker 环境下必须绕过 bridge 网络栈
同一宿主机上跑发布/订阅容器,延迟从 0.5ms 跳到 6–12ms,90% 是 Docker 默认 --network bridge 搞的鬼:veth → docker0 → iptables → NAT,每跳加 0.5–2ms。TCP 往返路径拉长,直接抬高 RTT,尤其对 pubsub.listen() 这种长轮询模式影响显著。
解决方式很直接:
- 改用
--network host,延迟回落至 0.3–0.8ms;但要注意:redis-cli必须连127.0.0.1:6379,不能用localhost(可能走 Unix socket 失败) - 若必须用 bridge,创建自定义网络:
docker network create mynet,让 Redis 容器和客户端容器都加入,并用容器名(如redis)互连,绕过 docker0 网桥 - 绝对避免让 Python 客户端通过
127.0.0.1:6379连宿主机 Redis——这会触发 Docker 的 port mapping,引入 DNAT 和 conntrack,延迟更不可控
丢包率只能靠客户端自埋点,Redis 本身不提供
Redis 没有原生「丢包数」指标,INFO PUBSUB 只返回当前订阅者数量,不记录任何溢出、驱逐或丢弃计数。redis_exporter 默认采集不到丢包率,因为它只解析 INFO 命令输出。
可行方案只有应用层补全:
- 发布端每发一条消息,往 Redis 的
Sorted Set写入:ZADD pubseq:order_updates <ts> msg:12345</ts> - 每个订阅线程收到消息后,立刻用
pipeline执行ZREM pubseq:order_updates msg:12345并记录成功数 - 定时任务(如每 10 秒)执行
ZCARD pubseq:order_updates,剩余数量即为「疑似丢失」消息数 - 注意:这个方法测的是「未被任一消费者处理」的消息,不是网络层丢包,但对业务已足够敏感
真正难处理的是慢消费者——它不报错、不断连,只是越积越多,把输出缓冲区悄悄撑爆。这种问题不会出现在压测报告里,只会在线上凌晨三点突然爆发。











