SSE、WebSocket 连接丢 Redis 里?那可踩大坑了!

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

这两天整理了一个simonking-ws/stream-nexus项目,原本定位就是一个轻量级的应用,不想使用任何第三方的中间件。

介绍项目的相关文档发不出后,有老铁问到能否集群部署。显然目前是不能的满足的。本节就聊聊实现的思路,后面时间允许的话也安排上。

02 实现思路

刚看这位老铁的提问,我本能的反应,这个简单呀,直接把客户端的连接从内存搬到中间件(Redis)里面去就好了。

2.1 简单测试

鉴于开发的习惯,总要先写个Demo测试一下。测试之后才发现根本行不通。

分别使用内存的对象和Redis中的对象发送,结果只收到内存对象的推送:

我们看看Redis中保存的是什么?

Redis对象中只保留了客户端中保留了超时时间,显然是很多参数丢失了。

2.2 原因

应该会有和我一样想法的老铁吧!哈哈哈......

归根结底是因为连接是"活的"对象,Redis 是"死的"键值存储,两者根本不是一回事。连接本质上是"内核态对象",序列化没用。

一条 SSE/WebSocket 连接至少由这些东西组成:

部分 归属 可序列化吗
TCP Socket fd / 端口号 OS 内核 ❌
TCP 收发缓冲区、滑动窗口状态 OS 内核 + 网卡驱动 ❌
TLS Session、加密密钥、序列号 协议栈 ❌
HTTP/1.1 解析状态、chunked 编码位置 Servlet 容器 ❌
SseEmitter / WebSocketSession 句柄 应用容器 ❌

一个连接从握手成功那一刻起,就和"建立它的那台机器的操作系统、JVM、Servlet 容器、网卡"绑死了。

当把 clientId → 8088机器的连接 这条映射关系序列化到 Redis 里,Redis 存的只是一条字符串,它指向的那根 TCP 还在原机器上。一旦那台机器宕机/重启/扩缩容,Redis 里这条映射就变成"幽灵指针"。

也就是说Redis存的只是"路由表",但真正的连接根本拿不走。

2.3 高可用方案

既然连接是基于内核、容器的,那就只能保存在当前的机器的进程中。高可用方案无非就是通过到各台机器触发自己的客户端推送消息即可。

可以通过Redis或者MQ等其他中间件,作为消息总线。如Redis

text 复制代码
 业务系统 ──HTTP POST──▶ /push
                              │
                              ▼
                       推送入口网关 (任一实例)
                              │
                              ▼
                     Redis Pub/Sub (channel: biz:order:paid)
                              │
            ┌─────────────────┼─────────────────┐
            ▼                 ▼                 ▼
        sse/ws-1         sse/ws-2             sse/ws-3
      本地连接表查找       本地连接表查找         本地连接表查找
            │                 │                 │
            ▼                 ▼                 ▼
         客户端A          客户端B           客户端C

关键设计:

  1. 推送入口统一化,业务系统只调一次推送接口
  2. Redis Pub/Sub 的 channel 划分:
    • biz:module:{bizModule} ------ 按业务模块广播
    • biz:client:{clientId} ------ 定向推送
    • 推送时同时发到两类 channel,各实例根据本地订阅关系决定是否落地。
  3. 本地查找:每台实例订阅全量 channel,收到消息后只在自己的连接表里命中目标

03 单体与集群

程序代码的高可用深入人心,面试更是逃不掉的八股文。很多人都觉得集群就是比单体的项目牛逼,的确是的,集群就是牛逼。

但是,这个要看真实的业务场景。比如天猫、京东这类大厂,单体项目自然无法支撑,集群是必然选择,大厂里面有无数牛逼的大佬专门通过中间件维护者集群的高可用。

而作为一般公司,业务量没有那么大时,单体项目也可以支撑。但是项目没有复杂的中间件,维护成本很低。如果硬要上集群,当然也没问题,只不过增加了维护的成本。

所以,单体未必差、集群未必好!适合自己的项目就行。系统不是设计出来了,是不断迭代演化而来的。

04 小结

我一直在想Stream Nexus要不要去支持集群,集群必然需要引入中间件。好纠结呀!

Redis似乎已经是很多企业一定有的中间件,通过Redis的Pub/Sub 广播当做消息总线似乎也是一种不错的选择。

后续根据情况,可以支持(但不必须)集群,提供多种选择,完善项目。希望对正在折腾推送服务的老铁们有帮助。

相关推荐
Pniubi4 小时前
HashMap的扩容和树化边界
java·开发语言
Mikko74 小时前
用 Java 复刻了高中时在塞班上玩的「熔岩矿车」:纯 Swing、零依赖、没有一张图片,一个 jar 双击就能玩
java·游戏开发·swing
知了学历孙老师4 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
Java爱好狂.5 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
秋饼5 小时前
LLM API密钥动态轮换实战:Spring Vault + Spring AI + 多Provider路由
java·ai·技术分享·后端开发
马良神笔6 小时前
2026 年Rag最近技术前沿总结
后端·python·django
程序猿DD6 小时前
同样10美元,OpenDesign和Command Code的DeepSeek V4.1 Flash额度到底有多少?
后端
福兮说7 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
高频因子挖掘机7 小时前
A股量化从日线升级到5分钟线:数据量增加后最先暴露哪些工程问题?
后端·github·api
高频因子挖掘机7 小时前
历史行情返回顺序变了,为什么按行号计算指标很危险?
后端·github·api