bigywater.tech
Redis · 49

Pub/Sub 丢消息问题

Pub/Sub 是「发后即忘」:消息不落盘、没有确认、也没有重发。这句话人人会背,但丢消息具体是怎么发生的,丢多少?这篇在真实实例上复现四种情形:没人订阅、订阅者掉线、订阅者太慢被踢、服务器重启。

35 / 300
订阅者掉线约 0.5 秒时丢失的消息数(每 10 ms 一条,连续丢失)
第 746 条
订阅者不读时,发到这一条(1000 字节)触发 100kb 硬上限被断开
0
没有订阅者时 PUBLISH 的返回值,消息不保存

没有订阅者:消息立刻消失

实测 · 本机执行输出
> PUBLISH ch m0
(integer) 0
> PUBLISH ch m1
(integer) 1
订阅者收到: ['message', 'ch', 'm1']
订阅者断开后再发布:
> PUBLISH ch m2
(integer) 0
重新订阅:
0.3 秒内收到: 什么都没有(m2 不会补发)

PUBLISH 返回的是「这条消息被发给了几个订阅者」。没人订阅返回 0,消息就没了;订阅者断开后再发布,同样返回 0;重新订阅,0.3 秒内什么都收不到,不会补发。

订阅者掉线期间的缺口

实测 · 本机执行输出
发布 300 条;订阅者收到 265 条;缺失 35 条,区间 101–135(连续: True)

每 10 ms 发布一条,共 300 条;订阅者收到第 100 条后主动断开,0.5 秒后重连。结果收到 265 条、缺 35 条,缺的是连续的 101–135——正好是断开期间的那段。缺口长度 ≈ 掉线时间 ÷ 发布间隔(0.5 s ÷ 10 ms ≈ 50,实测 35,因为断开与重连本身也有耗时)。

订阅者太慢:被服务器断开

实测 · 本机执行输出
发了 746 条 1000 字节的消息;最后一次 PUBLISH 返回的订阅者数 = 0
PUBSUB NUMSUB big: ['big', 0]
INFO stats client_output_buffer_limit_disconnections: 0 → 1
服务器日志(节选):
  id=11 age=0 omem=102520 tot-mem=120240 cmd=subscribe … scheduled to be closed ASAP for overcoming of output buffer limits.

为了快速触发,我把 client-output-buffer-limit pubsub 调成 100kb 50kb 5(默认是 32mb 8mb 60),并让订阅者只订阅、完全不读。发了 746 条 1000 字节的消息后,输出缓冲区超过 100 KB 硬上限,服务器日志出现 scheduled to be closed ASAP for overcoming of output buffer limits,INFO stats 里的 client_output_buffer_limit_disconnections 从 0 变成 1,此后 PUBLISH 返回 0。前面那些还在缓冲区里、没发给订阅者的消息也就没了。默认值大得多,但只是把「被踢」推迟到缓冲区涨到 32 MB(或 8 MB 持续 60 秒),并没有消除。(本机 CONFIG GET client-output-buffer-limit 返回的默认值是 pubsub 33554432 8388608 60,即 32 MB / 8 MB / 60 秒。)

服务器重启

实测 · 本机执行输出
> PUBSUB NUMSUB ch3
1) "ch3"
2) (integer) 0
> PUBLISH ch3 x
(integer) 0
原订阅连接: EOFError closed

重启后 PUBSUB NUMSUB 是 0,原订阅连接读到 EOF。订阅关系存在连接上,不会持久化,重连后必须重新 SUBSCRIBE。

分步演示:四种丢消息

Pub/Sub 丢消息
本步涉及的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。为触发缓冲区上限,该实例的 client-output-buffer-limit pubsub 被显式设为 100kb 50kb 5
    • 没有验证:默认的 32mb 8mb 60 实际何时触发、集群和分片 Pub/Sub(SSUBSCRIBE)下的行为、客户端库的自动重连与重新订阅、RESP3 下的推送行为
    京ICP备2026067502号-1京公网安备11010502063487号