用户离开行情页 20 分钟,服务端还在给他推这条频道的 tick,一天多出 4 万条无效发送,频道的订阅者数只增不减。乍看像缓存或者权限的锅,追下去往往不是。

roomSubs.forEach((set, ch) => {
  set.delete(ws);
  if (!set.size) roomSubs.delete(ch);
});

追下去是这么回事:unsubscribe 消息到了只改了客户端的本地标记,服务端的 Map 没删。页面切走时组件卸载了,socket 是全局的,没人负责发取消。

「订阅关系未清理」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。

线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

具体做法是,订阅用引用计数 一个频道被几个人订就记几;unsubscribe 减到 0 才真正从 Map 里删;onclose 里把这连接的所有订阅一次性清掉;加订阅总数指标 涨了就看谁没退。

验收就看一点,离开页面 30 秒后该频道订阅者数降到 0 日志里不再出现这条推送。

作者 9823189