redis pub/sub 不能替代长轮询实现可靠实时通知,仅适用于消息不丢、不重、无需回溯的轻量场景;因其无持久化、无确认、无历史回溯能力,可靠性要求高时反成故障点。

Redis Pub/Sub 能直接替代长轮询做实时通知,但只适用于“消息不丢、不重、不需回溯”的轻量场景;一旦需要可靠性保障,它反而会成为故障点。
为什么长轮询在实时通知里越来越不被接受
长轮询本质是客户端反复发 HTTP 请求,服务端卡住响应直到有新数据或超时。它的问题不是“写不出来”,而是:
- 每个连接都占一个线程/协程,5000 个用户在线 ≈ 5000 个并发连接,网关和后端压力陡增
- 平均延迟 = 轮询间隔 / 2(比如 2s 轮询,平均延迟 1s),无法做到真正“即时”
- 网络抖动或客户端切后台时,容易漏掉一次响应,还得靠客户端自己补逻辑去对状态
- HTTP 连接频繁建立销毁,TCP 握手、TLS 协商开销不可忽视,尤其在移动弱网下
Redis Pub/Sub 的实际接入链路怎么搭才不翻车
Pub/Sub 不是“连上 Redis 就能推消息”,它必须嵌入到你的服务架构中。典型可靠链路是:SSE 或 WebSocket 连接 → 后端服务(持有订阅)→ Redis SUBSCRIBE → 收到 PUBLISH 后触发推送。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 别让业务服务直接
SUBSCRIBE:每个服务实例都要单独订阅同一频道,消息会被重复推送给所有实例,再由你手动去重或路由,极易出错 - 用
pubsub_channels字典结构确认订阅是否生效:执行INFO PUBLISH或PUBSUB CHANNELS *,看返回是否包含你的频道名 - 注意
SUBSCRIBE是阻塞命令,不能混在普通请求处理线程里调用;Java 用JedisPubSub,Go 用redis.Conn.Subscribe,都得走独立 goroutine / 线程 - 如果后端是多节点部署,必须确保每台机器都运行一个订阅者,否则部分用户收不到通知——Pub/Sub 本身不负责跨进程分发
对比长轮询,Pub/Sub 在哪些地方真能省事
它省的不是代码行数,而是系统性负担:
- 连接数归零:长轮询 5000 用户 ≈ 5000 连接;SSE + Pub/Sub 下,连接数仍为 5000,但后端不再需要维持“等待消息”的阻塞逻辑,资源利用率翻倍提升
- 延迟压到毫秒级:
PUBLISH到onMessage触发通常在 0.2ms 内(见 2025 年性能测试数据),比最激进的 500ms 轮询还快 2 倍以上 - 断线重连交给浏览器:用
EventSource时,onerror触发后浏览器自动重连,不用你实现指数退避、session 恢复等逻辑 - 发布端彻底无感:业务代码只需调一次
PUBLISH "notify:123" "{...}",不用管谁在线、谁掉线、谁该收到
最容易被忽略的三个硬伤
很多人上线后才发现问题,不是 Pub/Sub 不好,而是没看清它的边界:
-
PUBLISH返回值永远是接收者数量,不是“发送成功”:如果此时没人SUBSCRIBE,返回 0 —— 但消息已永久丢失,Redis 不存、不重试、不告警 - 没有消息确认机制:
onMessage执行失败(比如 JSON 解析异常、空指针),Pub/Sub 完全不知情,也不会重发 - 频道名拼错=静默失效:写成
"notif:123"而不是"notify:123",发布和订阅根本不在一个频道,debug 时只能靠PUBSUB NUMSUB notify:123手动查
所以,如果你的通知要保证“至少一次送达”,或者要支持用户离线重连后拉取历史消息,就别碰 Pub/Sub —— 直接上 Stream 或专业队列。它适合的,只是“此刻在线的人,看到就行”的那一类通知。










