WebSocket与长连接恢复
HTTP场景下恢复会话靠Cookie,但WebSocket这类长连接,断开后客户端和服务端都不会自动重连,需要应用层做主动续接。
WebSocket连接恢复的通用模式是心跳、重连握手、重新订阅三步走:
- 客户端维护一个定时器,每30秒发送一次心跳帧,比如
{"type":"ping"}。 - 服务端在两次心跳间隔内没收到消息,主动关闭连接。
- 客户端监听
onclose事件,触发指数退避重连逻辑,最多重试5次。 - 重连成功后,客户端必须重新发送认证信息和订阅指令,服务端据此重新绑定会话上下文。
代码层面,前端的处理逻辑可以这么写:
function connect() {
const ws = new WebSocket("wss://api.example.com/ws");
ws.onclose = () => {
setTimeout(() => {
reconnectAttempts++;
connect();
}, Math.pow(2, reconnectAttempts) 1000);
};
}
注意这里的细节:服务端不能只靠TCP的FIN包感知断线,在很多网络环境下,异常断开不会触发任何标志,所以心跳机制是必要的,这也是为什么所有生产级别的WebSocket服务都要求客户端有心跳帧。
登录态的续接:令牌派生的恢复方案
会话续不上的终极防线是去掉“会话”这个概念,改用无状态令牌,JWT(JSON Web Token)方案下,用户身份信息全在客户端持有的Token里,服务端不用保存任何状态,自然不存在“会话被丢弃”的问题。

这种方式解决的是会话丢失后用户需要重新登录的痛点,客户端在拿到新连接后,把旧Token重新放入Authorization请求头,服务端校验签名后直接放行。
但JWT方案也有代价:服务端无法主动吊销一个已签发的Token,除非引入黑名单机制或者Token版本号,业内专家建议,在安全性要求高的场景下,用“短期Token + 刷新令牌”组合,短期Token过期后通过刷新令牌换取新Token,避免用户频繁输入密码。
下面是一个刷新令牌的标准时序:
- 客户端携带刷新令牌请求
/auth/refresh。 - 服务端验证刷新令牌是否有效、未过期、不在吊销列表。
- 服务端签发新Access Token,同时轮换刷新令牌本身。
- 客户端用新Token重建立连接,旧Token立即失效。
这种方式比单纯延长会话超时时间安全得多,也彻底绕开了“会话存储在哪台机器上”的问题。
具体怎么排查会话为何续不上
如果线上已经出现“连接断了但会话续不上”的报警,排查路径建议按以下顺序走,不要上来就改代码:

- 第一步:确认客户端Cookie是否还在,浏览器开发者工具里看
Application面板,检查JSESSIONID是否存在,是否在同一个域名下、Path是否正确。 - 第二步:确认服务端会话是否存活,如果用的是Redis存储,查看Redis里是否有对应Key,没有Key就说明会话已被删,重点看超时配置和主动删除逻辑。
- 第三步:确认请求是否被负载均衡转发,在Nginx日志里看同一个Cookie是否落到同一台机器,如果没有,说明粘滞配置失效。
- 第四步:确认时间是否同步,如果客户端和服务端时间差超过5分钟,JWT和Cookie的过期校验都会出现问题,这属于常见低级隐患。
概括顺序就是:先看客户端有没有身份凭证,再看服务端有没有存状态,最后看中间链路有没有把请求转错地方,大多数“会话续不上”的问题出在第二步的存储选型和第三步的路由规则上,而不是客户端本身。
最常见的三个会话续接问题,直接给答案
会话丢失后要不要让用户重新登录?
取决于你存的是什么,如果只是用户ID和昵称,放在Token里就能免登录;如果会话里存了完整的用户角色列表和权限位,那必须从数据库或Redis重新加载,被动方案是延长超时时间,主动方案是引入共享存储,让新节点能读到旧状态。

iOS App和浏览器在会话恢复上有什么差别?
浏览器的Session由Cookie管理,会自动携带,服务器端基本无感,iOS App里没有Cookie机制,客户端必须手动把SessionID放到请求头里,App冷启动后SessionID是否持久化存储,直接决定了会话能否续上,用UserDefaults或Keychain保存SessionID,是iOS端续接会话的常用做法。
Redis保存的会话数据会不会成为新的单点?
Redis挂掉确实会让所有在线会话失效,稳妥的做法是给Redis做主从+哨兵架构,或者直接使用云厂商提供的托管实例,如果是中小型项目,更务实的做法是用本地存储做兜底,Redis不可用时降级为内存会话,保证基础功能可用,据行业共识,Redis自身的可用性远高于普通应用节点,所以这项依赖是值得的。
连接续上的本质,是让身份和状态分离,身份由客户端持有,状态由共享存储承载,掌握这条原则,无论将来面对的是WebSocket、HTTP还是QUIC,都能快速定位会话恢复的入口,先用粘滞解决小规模问题,再引入共享存储支撑集群扩展,最后用无状态令牌解放服务端内存,这是从单体到分布式架构演进中的一条清晰路径。