feat: 增加ws重连机制

This commit is contained in:
Baobhan Sith
2025-04-16 15:19:06 +08:00
parent a83b346956
commit 84f0368811
4 changed files with 68 additions and 7 deletions
@@ -24,6 +24,11 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
const messageHandlers = new Map<string, Set<MessageHandler>>(); // 此实例的消息处理器注册表
const instanceSessionId = sessionId; // 保存会话 ID 用于日志
const instanceDbConnectionId = dbConnectionId; // 保存数据库连接 ID
let reconnectAttempts = 0; // 重连尝试次数
const maxReconnectAttempts = 5; // 最大重连次数
let reconnectTimeoutId: ReturnType<typeof setTimeout> | null = null; // 重连定时器 ID
let lastUrl = ''; // 保存上次连接的 URL
let intentionalDisconnect = false; // 标记是否为用户主动断开
// --- End Instance State ---
/**
@@ -60,11 +65,47 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
}
};
/**
* 安排 WebSocket 重连尝试
*/
const scheduleReconnect = () => {
if (intentionalDisconnect) return; // 如果是主动断开,则不重连
if (reconnectAttempts >= maxReconnectAttempts) {
console.log(`[WebSocket ${instanceSessionId}] 已达到最大重连次数 (${maxReconnectAttempts}),停止重连。`);
statusMessage.value = getStatusText('reconnectFailed');
connectionStatus.value = 'error'; // 标记为错误状态
return;
}
reconnectAttempts++;
// 指数退避延迟 (例如: 2s, 4s, 8s, 16s, 32s)
const delay = Math.pow(2, reconnectAttempts) * 1000;
console.log(`[WebSocket ${instanceSessionId}] 连接丢失,将在 ${delay / 1000} 秒后尝试第 ${reconnectAttempts} 次重连...`);
statusMessage.value = getStatusText('reconnecting', { attempt: reconnectAttempts, delay: delay / 1000 });
connectionStatus.value = 'connecting'; // 更新状态为正在连接
if (reconnectTimeoutId) clearTimeout(reconnectTimeoutId); // 清除旧的定时器
reconnectTimeoutId = setTimeout(() => {
if (!intentionalDisconnect && lastUrl) { // 再次检查是否主动断开
connect(lastUrl);
}
}, delay);
};
/**
* 建立 WebSocket 连接
* @param {string} url - WebSocket 服务器 URL
*/
const connect = (url: string) => {
lastUrl = url; // 保存 URL 以便重连
intentionalDisconnect = false; // 重置主动断开标记
if (reconnectTimeoutId) {
clearTimeout(reconnectTimeoutId); // 清除可能存在的重连定时器
reconnectTimeoutId = null;
}
// 防止重复连接同一实例
if (ws.value && (ws.value.readyState === WebSocket.OPEN || ws.value.readyState === WebSocket.CONNECTING)) {
console.warn(`[WebSocket ${instanceSessionId}] 连接已打开或正在连接中。`);
@@ -81,6 +122,7 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
ws.value.onopen = () => {
console.log(`[WebSocket ${instanceSessionId}] 连接已打开。`);
reconnectAttempts = 0; // 连接成功,重置尝试次数
statusMessage.value = getStatusText('wsConnected');
// 状态保持 'connecting' 直到收到 ssh:connected
// 发送后端所需的初始连接消息,包含数据库连接 ID
@@ -140,18 +182,32 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
dispatchMessage('internal:error', event, { type: 'internal:error' });
isSftpReady.value = false;
ws.value = null; // 清理实例
// 如果不是主动断开,尝试重连
if (!intentionalDisconnect) {
scheduleReconnect();
}
};
ws.value.onclose = (event) => {
console.log(`[WebSocket ${instanceSessionId}] 连接已关闭: Code=${event.code}, Reason=${event.reason}`);
if (connectionStatus.value !== 'disconnected' && connectionStatus.value !== 'error') {
// 只有在非错误状态下才更新为 disconnected
if (connectionStatus.value !== 'error') {
connectionStatus.value = 'disconnected';
statusMessage.value = getStatusText('wsClosed', { code: event.code });
// 如果不是主动断开,显示尝试重连的消息
if (!intentionalDisconnect && event.code !== 1000) {
statusMessage.value = getStatusText('wsClosedWillRetry', { code: event.code });
} else {
statusMessage.value = getStatusText('wsClosed', { code: event.code });
}
}
dispatchMessage('internal:closed', { code: event.code, reason: event.reason }, { type: 'internal:closed' });
isSftpReady.value = false;
ws.value = null; // 清理实例引用
// 不自动清除处理器,以便在重连时可能复用
// 如果不是主动断开 (code 1000),尝试重连
if (!intentionalDisconnect && event.code !== 1000) {
scheduleReconnect();
}
};
} catch (err) {
console.error(`[WebSocket ${instanceSessionId}] 创建 WebSocket 实例失败:`, err);
@@ -166,6 +222,11 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
* 手动断开此 WebSocket 连接
*/
const disconnect = () => {
intentionalDisconnect = true; // 标记为主动断开
if (reconnectTimeoutId) {
clearTimeout(reconnectTimeoutId); // 清除重连定时器
reconnectTimeoutId = null;
}
if (ws.value) {
console.log(`[WebSocket ${instanceSessionId}] 手动关闭连接...`);
if (connectionStatus.value !== 'disconnected') {