ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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业务系统 ──HTTP POST──▶ /push │ ▼ 推送入口网关 (任一实例) │ ▼ Redis Pub/Sub (channel: biz:order:paid) │ ┌─────────────────┼─────────────────┐ ▼ ▼ ▼ sse/ws-1 sse/ws-2 sse/ws-3 本地连接表查找 本地连接表查找 本地连接表查找 │ │ │ ▼ ▼ ▼ 客户端A 客户端B 客户端C关键设计推送入口统一化业务系统只调一次推送接口Redis Pub/Sub 的 channel 划分biz:module:{bizModule}—— 按业务模块广播biz:client:{clientId}—— 定向推送推送时同时发到两类 channel各实例根据本地订阅关系决定是否落地。本地查找每台实例订阅全量 channel收到消息后只在自己的连接表里命中目标03 单体与集群程序代码的高可用深入人心面试更是逃不掉的八股文。很多人都觉得集群就是比单体的项目牛逼的确是的集群就是牛逼。但是这个要看真实的业务场景。比如天猫、京东这类大厂单体项目自然无法支撑集群是必然选择大厂里面有无数牛逼的大佬专门通过中间件维护者集群的高可用。而作为一般公司业务量没有那么大时单体项目也可以支撑。但是项目没有复杂的中间件维护成本很低。如果硬要上集群当然也没问题只不过增加了维护的成本。所以单体未必差、集群未必好适合自己的项目就行。系统不是设计出来了是不断迭代演化而来的。04 小结我一直在想Stream Nexus要不要去支持集群集群必然需要引入中间件。好纠结呀Redis似乎已经是很多企业一定有的中间件通过Redis的Pub/Sub广播当做消息总线似乎也是一种不错的选择。后续根据情况可以支持但不必须集群提供多种选择完善项目。希望对正在折腾推送服务的老铁们有帮助。
返回列表