WebSocket本身不支持消息置顶和红点语义,二者必须由业务层实现:红点依赖用户上下文与持久化未读计数(如Redis),置顶需前端解析isTop字段并独立处理,且两者正交不可混用。

WebSocket 本身不支持“消息置顶”语义,红点提示必须靠业务层维护状态,不能只靠连接通断或收发动作自动推导。
为什么 onMessage 收到消息不等于要亮红点
前端通过 WebSocket.onmessage 能拿到原始数据,但是否显示红点、是否算“未读”,取决于当前用户上下文:
- 用户正在查看该类消息列表 → 收到新消息不该亮红点(已聚焦)
- 消息属于已读过的会话/模块 → 即使收到推送,也不应叠加未读计数
- 后端推送的是系统广播(如“全员公告”),但用户已手动关闭该类型通知 → 红点逻辑需查用户偏好配置
简单地在 onMessage 里无条件执行 setUnreadCount(count + 1),会导致红点与实际状态严重脱节。
@ServerEndpoint 路径带 {userId} 是红点隔离的前提
如果所有客户端都连同一个 endpoint(如 /ws),服务端只能做广播,无法按人定向推送未读状态。必须让连接路径携带身份标识:
- 推荐写法:
@ServerEndpoint("/ws/{userId}"),配合 Spring 的@PathParam("userId") - 连接时前端传真实登录态 ID(不要用随机 token 或 session id 替代)
- 服务端用
ConcurrentHashMap<string session></string>按userId存连接,而不是用CopyOnWriteArraySet扁平管理
否则你后续想给张三单独发“你有 2 条未读审批”,根本找不到他的 Session 实例。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
红点数字怎么同步?别只靠 WebSocket 推送
红点本质是“未读数”,它需要持久化、可查询、可清零。纯靠 WebSocket 消息传递数字极易丢失或错乱:
- 用户 A 切后台再切回,WebSocket 可能重连,但重连期间的增量消息没收到 → 显示数字比实际少
- 用户 B 在手机和桌面同时登录,两个终端各自增减计数 → 出现不同步
- 服务端重启,内存里的计数全丢 → 红点消失,但数据库里未读记录还在
正确做法是:红点数字始终以 Redis 或 DB 中的 unread_count:userId 为准,WebSocket 只负责“触发刷新”:
- 后端写入新消息时:
INCR unread_count:123 - 前端页面加载或切回前台时:
GET unread_count:123→ 更新红点 - 用户点击进入消息页时:
SET unread_count:123 0,并广播{"type":"clear","moduleId":"approval"}给所有同用户连接
“置顶”得靠前端自己管,WebSocket 不提供优先级字段
WebSocket 协议没有 priority、sticky 或 top 这类消息头。所谓“置顶”,其实是业务规则:
- 后端推送消息时,加一个
"isTop": true字段(例如审批加急、故障告警) - 前端收到后,不直接进普通消息队列,而是插入数组头部或单独渲染为 Banner
- 注意:置顶状态通常有时效(比如 30 分钟后自动取消),这个逻辑必须由前端定时器或后端 TTL 缓存控制,不能依赖 WebSocket 连接生命周期
最容易被忽略的一点:红点和置顶是两个正交维度。一条消息可以既置顶又计入未读数,也可以置顶但已被用户标记为已读 —— 它们的判断依据完全不同,不能混在同一个 if 分支里处理。

















