WebSocket 管理
Websocket 管理模块其实是整个项目的核心部分,主要用来获取交易所实时信息,这部分采用了单例模式,便于全局统一管理维护 WebSocket,避免多实例连接、状态不一致和重复重连的问题。
由于 WebSocket 是运行在Chrome MV3的background Service Worker中,不能保证长期常驻,同时还要把数据传递到popup和contentUI 层中,做了一层封装,统一处理连接管理、状态判断和异常恢复。
1、基础功能
- connect(): 初始连接
- disconnect(): 主动断开连接
- onMessage(): 消息处理回调
- onOpen(): 连接成功回调
- onClose(): 连接关闭回调
- onError(): 连接错误回调
2、指数退避重连策略
问题:
- 在 MV3 下 background 是 Service Worker,本身不保证长期常驻,网络切换或者空闲回收都可能导致 WebSocket 中断;
- 在网络不稳定或交易所限流场景下,如果立即重连话,很容易产生连接风暴问题。
方案:采用指数退避重连策略来防止出现以上情况。这样就可以在网络短暂抖动(比如:切换网络),可以快速恢复 Websocket 连接,而在持续异常时,主动降频,减少资源消耗和降低被风控风险。
- scheduleRetry():
/**
* 调度重试连接
*/
private scheduleRetry(): void {
// 主动断开,不重试
if (this.isManualDisconnect) {
console.log('[WsManager] 主动断开,不进行重试');
return;
}
// 没有连接信息,无法重试
if (!this.currentExchange || !this.currentTokenList.length) {
console.log('[WsManager] 没有连接信息,无法重试');
return;
}
// 先检查是否已经在冷却模式
if (this.inCooldownMode) {
console.log('[WsManager] 已在冷却模式,跳过重试');
return;
}
// 增加重试计数
this.retryCount++;
// 检查是否超过最大重试次数,进入冷却模式
if (this.retryCount > this.config.maxRetries) {
console.log(`[WsManager] 已达到最大重试次数 (${this.config.maxRetries}),进入冷却模式`);
this.enterCooldownMode();
return;
}
const delay = this.getRetryDelay();
console.log(`[WsManager] 第 ${this.retryCount}/${this.config.maxRetries} 次重试,${delay / 1000}秒后执行...`);
this.clearRetryTimer();
this.retryTimer = setTimeout(() => {
console.log(`[WsManager] 开始第 ${this.retryCount} 次重连...`);
this.isManualDisconnect = false; // 确保重连时允许后续重试
this.connect(this.currentExchange!, this.currentTokenList);
}, delay);
}
3、假死检测(连接存在但无数据)
问题:在使用 WebSocket 的过程中,我遇到一个比较隐蔽的问题:有时把电脑息屏、浏览器进入后台或切换网络后,WebSocket 连接状态仍然后 OPEN 状态,但实际上已经无法接收到任何数据,而且也没有触发close和error回调,导致页面上网络状态一直显示正常,但是数据却没有变化。
原因:在浏览器空闲或后台状态下,浏览器内部会主动降级或挂起长连接的调度,但并不一定触发 WebSocket 的回调,从而导致“假死”状态。
方案:如果仅通过 WebSocket 状态来判断连接是否正常,就会出现上面的问题;所以这里我又基于 WebSocket 是否持续接收到真实行情数据,来判断 WebSocket 是否处于健康状态。一旦在设定时间内没有收到有效的数据,就判定为假死状态进而可以触发重连策略。避免页面长期处于“看似正常、实际不可用”的状态。
- detectAndHandleStaleConnection()
/**
* 检测并处理 WebSocket 假死状态
* 如果连接存在但长时间没有收到消息,则更新状态为 OFFLINE 并强制重连
* @param staleThreshold 假死阈值(毫秒),默认 60000 (1分钟)
*/
detectAndHandleStaleConnection(staleThreshold: number = 60000): boolean {
const now = Date.now();
const timeSinceLastMessage = now - this.lastMessageAt;
// 如果 WebSocket 显示连接,但超过阈值时间没有收到消息,判定为假死
if (this.isConnected() && timeSinceLastMessage > staleThreshold) {
console.warn(`[WsManager] 检测到 WebSocket 假死: ${Math.floor(timeSinceLastMessage / 1000)}秒内无消息`);
// 更新状态为 OFFLINE
this.setDataStatus(DataStatus.OFFLINE);
return true; // 返回 true 表示检测到假死
}
return false; // 返回 false 表示连接正常
}
4、冷却模式(频繁失败后降低重试频率)
冷却模式其实是为了解决网络不可恢复时的兜底方案,比如交易所的 WebSocket API 只有在特定网络下才能使用,比如关闭 VPN 或者断开网络就会无法连接成功。这种情况下,重连次数很快就会被消耗完,而当网络恢复的时候,还需要手动刷新来启动插件。因此设计了冷却模式,当重连次数达到阈值后就会进入冷却模式,直到网络恢复从而自动连接。
- enterCooldownMode()
/**
* 进入冷却模式
* - 状态变为 OFFLINE
* - 每隔 cooldownInterval 尝试重连一次
*/
private enterCooldownMode(): void {
console.log('this.inCooldownMode', this.inCooldownMode);
if (this.inCooldownMode) return;
this.inCooldownMode = true;
this.setDataStatus(DataStatus.OFFLINE);
console.log(`[WsManager] 进入冷却模式,每 ${this.config.cooldownInterval / 1000}s 尝试重连一次`);
// 启动冷却定时器
this.cooldownTimer = setInterval(() => {
console.log('[WsManager] 冷却模式:尝试重连...');
this.attemptCooldownReconnect();
}, this.config.cooldownInterval);
}
📝 写在最后
目前这个插件只有我在本地使用,也还可能存在一些 bug。
项目已放在 GitHub 上: github.com/CHYYHC123/c…
release 目录下包含已打包好的 Chrome 扩展,可直接安装体验。 链接:github.com/CHYYHC123/c…
目前的功能设计更多是基于个人使用习惯,如果你在使用过程中有更好的想法、发现问题,或者有不同的设计思路,欢迎一起交流讨论。