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