基于Spring Boot+Websocket的指定Topic订阅者数量统计方案咨询
在线聊天室成员计数方案分析
针对你的需求,下面逐个分析你提到的方案,并给出适配场景的建议:
1. Singleton(单例内存存储)
- 适用场景:仅单实例部署的小型应用
- 优点:实现最简单,读写完全在内存,延迟极低,不需要额外依赖。
- 缺点:
- 集群部署时完全失效:每个实例维护独立的计数器,用户连接到不同节点会导致计数不一致。
- 服务重启/崩溃后,计数器直接清零,无法恢复历史在线数据。
- 无法自动处理用户异常断开(比如客户端直接关闭浏览器)的情况,除非手动在WebSocket断开事件里维护计数,但单例本身不提供持久化或分布式一致性保障。
2. MongoDB存储
- 可行,但不是最优解
- 优点:
- 支持持久化,服务重启后可以恢复计数。
- 集群部署时能保证数据一致性(MongoDB副本集/分片集群)。
- 可以用原子操作
$inc来保证计数的准确性,避免并发更新问题。
- 缺点:
- 高频读写场景下性能不如内存级存储:每次用户进出都要写磁盘(即使是MongoDB的内存映射引擎),延迟比Redis或内存单例高。
- 如果需要实时显示在线人数,每次前端请求都查DB会增加不必要的开销,仍需结合缓存做优化。
3. 分布式缓存(比如Redis)
- 推荐方案,尤其适合集群场景
- 为什么适合:
- Redis的
INCR/DECR是原子操作,完美适配高频变更的计数器场景,单实例QPS轻松达到数万级,延迟极低。 - 支持集群部署,天然解决多实例WebSocket节点的计数一致性问题。
- 可选持久化(RDB/AOF),即使缓存重启,也能恢复大部分计数数据。
- Redis的
- 实现要点:
- 用户加入聊天室时,调用
INCR chat:online:{channelId}。 - 用户断开WebSocket时(通过
HandlerInterceptor的afterCompletion方法),调用DECR chat:online:{channelId}。 - 前端获取在线人数时,直接从Redis读取
GET chat:online:{channelId},实时性拉满。
- 用户加入聊天室时,调用
额外注意事项
- 一定要处理用户异常断开的情况:WebSocket连接可能因为网络波动、客户端崩溃等原因异常关闭,你需要在
SimpUserRegistry或者自定义拦截器里监听会话销毁事件,及时递减计数器,避免计数不准。 - 如果你的应用目前是单实例,但未来有集群扩容计划,直接用Redis可以避免后续重构成本。
内容的提问来源于stack exchange,提问作者Allteran
相关产品推荐
相关产品推荐

