IM系统上线之后,最容易被忽略的一类问题不是发送失败,而是“看起来在线,实际上已经断了”。这种情况用户感知很直接:消息发不出去、收不到、需要反复重启应用。
宠友信息在「宠友IM」源码里,对WebSocket连接这一层做了不少细节处理,没有复杂协议,但在心跳检测、断线重连、连接状态维护这几个点上比较扎实。
这篇只拆一个主题:WebSocket连接稳定性优化。

一、长连接为什么会“假在线”
很多开发阶段的WebSocket实现是这样的:
- 建立连接
- 收发消息
- 断开再连
问题在于:TCP连接不等于真实在线状态
常见导致“假在线”的情况:
- 手机切后台,系统暂停网络
- 网络切换(WiFi → 4G)
- 路由器NAT回收连接
- 服务端连接未及时释放
这类问题不会触发onClose,服务端依然认为用户在线。
二、心跳机制设计(核心)
宠友IM没有使用复杂协议,而是采用最常见的一种:
👉 客户端主动心跳 + 服务端检测超时
基本策略:
- 客户端每30秒发送一次ping
- 服务端记录最后活跃时间
- 超过一定时间未收到心跳 → 判定离线
服务端核心逻辑:
// 简化示例:记录用户最后心跳时间
private static final ConcurrentHashMap<Long, Long> HEARTBEAT_MAP = new ConcurrentHashMap<>();
public void onHeartbeat(Long userId) {
HEARTBEAT_MAP.put(userId, System.currentTimeMillis());
}
配合定时任务扫描:
// 超时检测(60秒未心跳判定离线)
public void checkTimeout() {
long now = System.currentTimeMillis();
HEARTBEAT_MAP.forEach((userId, lastTime) -> {
if (now - lastTime > 60000) {
// 关闭连接 + 清理资源
disconnect(userId);
}
});
}
这种方式简单,但足够稳定。
三、为什么不能只依赖WebSocket事件
很多实现只依赖:
- onOpen
- onClose
- onError
问题在于:
👉 这些事件并不可靠
比如:
- 用户断网 → 服务端可能长时间收不到onClose
- 网络抖动 → 连接处于半开状态
宠友IM的处理方式是:
- WebSocket事件只是“参考”
- 心跳机制才是“最终判定”
四、断线重连策略(客户端关键逻辑)
断线不可避免,关键是重连策略。
常见错误做法:
- 立即重连
- 无限重试
结果:
- 短时间内大量请求
- 服务端压力暴涨
宠友IM采用:
👉 指数退避重连
逻辑:
- 第1次失败:1秒后重连
- 第2次失败:2秒
- 第3次失败:4秒
- 上限:30秒
避免雪崩。
客户端伪代码:
let retry = 0;
function reconnect() {
const delay = Math.min(30000, Math.pow(2, retry) * 1000);
setTimeout(() => {
connect();
retry++;
}, delay);
}
这个策略在网络波动场景下非常关键。
五、多端登录下的连接管理
宠友IM支持:
- APP
- 小程序
- H5
- PC
多端意味着:
👉 一个用户会有多个连接
服务端结构设计不是:
userId -> session
而是:
userId -> {设备类型 -> session}
这样可以做到:
- 不同端独立在线
- 消息可以同步推送多个端
同时避免:
- 新设备登录踢掉旧连接

六、连接状态同步问题
在单机下维护连接没问题,但一旦多节点部署:
- A服务器有连接
- B服务器不知道
宠友IM没有做复杂网关,而是用简单方案:
👉 Redis记录在线状态 + 广播机制
处理方式:
- 用户上线 → 写Redis
- 用户下线 → 删除Redis
- 发送消息时判断目标是否在线
这样可以做到:
- 跨节点识别在线状态
- 简单扩展
七、连接资源清理
连接不及时释放,会带来两个问题:
- 内存泄漏
- 连接数爆满
宠友IM的处理策略:
- onClose清理
- 心跳超时强制清理
- 异常捕获统一关闭
重点在于:不要依赖单一入口释放资源
八、消息发送失败处理
WebSocket发送不是100%成功:
- 连接存在但不可写
- 网络瞬断
宠友IM没有假设“发送一定成功”,而是:
👉 失败后走降级路径
策略:
- 发送失败 → 写入离线消息
- 用户下次上线拉取
这样可以保证:
- 消息不丢
- 用户体验一致
九、线上遇到的几个典型问题
1)频繁断开重连
原因:
- 心跳间隔过短
- 服务端超时过严
调整:
- 心跳30秒
- 超时60~90秒
2)连接暴涨
原因:
- 客户端异常重连
- 没有限流
处理:
- 增加连接频率限制
- 同IP限流
3)消息延迟
原因:
- 连接处于半开状态
- 数据未及时发送
解决:
- 心跳检测及时断开
- 强制重连
4)多端状态错乱
原因:
- 一个端断开,误删全部状态
解决:
- 按设备维度维护连接
- 独立管理生命周期

十、这类WebSocket优化的核心思路
宠友信息这套实现没有复杂协议,也没有引入额外网关层,但有几个关键点:
- 用心跳替代连接状态判断
- 用重连策略避免流量冲击
- 用简单结构支持多端在线
IM系统稳定性问题,本质不在“用不用WebSocket”,而在于:
👉 是否正确处理“连接不可靠”这件事

747

被折叠的 条评论
为什么被折叠?



