|
@@ -76,6 +76,8 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
private subscriptions: Set<string> = new Set();
|
|
private subscriptions: Set<string> = new Set();
|
|
private isConnected: boolean = false;
|
|
private isConnected: boolean = false;
|
|
|
|
+ private lastConnectTime: number = 0;
|
|
|
|
+ private consecutiveFailures: number = 0;
|
|
|
|
|
|
constructor(config: PacificaWebSocketConfig) {
|
|
constructor(config: PacificaWebSocketConfig) {
|
|
super();
|
|
super();
|
|
@@ -84,7 +86,7 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
apiKey: config.apiKey || '',
|
|
apiKey: config.apiKey || '',
|
|
reconnectInterval: config.reconnectInterval || 5000,
|
|
reconnectInterval: config.reconnectInterval || 5000,
|
|
heartbeatInterval: config.heartbeatInterval || 30000,
|
|
heartbeatInterval: config.heartbeatInterval || 30000,
|
|
- maxReconnectAttempts: config.maxReconnectAttempts || 10
|
|
|
|
|
|
+ maxReconnectAttempts: config.maxReconnectAttempts || 100 // 增加到100次
|
|
};
|
|
};
|
|
this.logger = Logger.getInstance();
|
|
this.logger = Logger.getInstance();
|
|
}
|
|
}
|
|
@@ -106,7 +108,9 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
this.ws.on('open', () => {
|
|
this.ws.on('open', () => {
|
|
this.logger.info('Connected to Pacifica WebSocket');
|
|
this.logger.info('Connected to Pacifica WebSocket');
|
|
this.isConnected = true;
|
|
this.isConnected = true;
|
|
|
|
+ this.lastConnectTime = Date.now();
|
|
this.reconnectAttempts = 0;
|
|
this.reconnectAttempts = 0;
|
|
|
|
+ this.consecutiveFailures = 0; // 重置连续失败计数
|
|
this.startHeartbeat();
|
|
this.startHeartbeat();
|
|
this.emit('connected');
|
|
this.emit('connected');
|
|
resolve();
|
|
resolve();
|
|
@@ -122,15 +126,36 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
});
|
|
});
|
|
|
|
|
|
this.ws.on('close', (code: number, reason: Buffer) => {
|
|
this.ws.on('close', (code: number, reason: Buffer) => {
|
|
- this.logger.warn('WebSocket connection closed', { code, reason: reason.toString() });
|
|
|
|
|
|
+ const reasonStr = reason.toString();
|
|
|
|
+
|
|
|
|
+ // 检查是否是502错误或其他严重错误
|
|
|
|
+ const isFatalError = code === 502 || reasonStr.includes('502') ||
|
|
|
|
+ code === 503 || reasonStr.includes('503');
|
|
|
|
+
|
|
|
|
+ if (isFatalError) {
|
|
|
|
+ this.logger.error(`🚨 致命WebSocket错误 [${code}]: ${reasonStr}`);
|
|
|
|
+ this.consecutiveFailures++;
|
|
|
|
+ } else {
|
|
|
|
+ this.logger.warn('WebSocket connection closed', { code, reason: reasonStr });
|
|
|
|
+ }
|
|
|
|
+
|
|
this.isConnected = false;
|
|
this.isConnected = false;
|
|
this.stopHeartbeat();
|
|
this.stopHeartbeat();
|
|
- this.emit('disconnected', { code, reason: reason.toString() });
|
|
|
|
|
|
+ this.emit('disconnected', { code, reason: reasonStr });
|
|
this.handleReconnect();
|
|
this.handleReconnect();
|
|
});
|
|
});
|
|
|
|
|
|
this.ws.on('error', (error: Error) => {
|
|
this.ws.on('error', (error: Error) => {
|
|
- this.logger.error('WebSocket error', {}, error);
|
|
|
|
|
|
+ const errorMsg = error.message || error.toString();
|
|
|
|
+
|
|
|
|
+ // 检查是否是502错误
|
|
|
|
+ if (errorMsg.includes('502') || errorMsg.includes('503')) {
|
|
|
|
+ this.logger.error(`🚨 致命WebSocket错误: ${errorMsg}`);
|
|
|
|
+ this.consecutiveFailures++;
|
|
|
|
+ } else {
|
|
|
|
+ this.logger.error('WebSocket error', {}, error);
|
|
|
|
+ }
|
|
|
|
+
|
|
this.emit('error', error);
|
|
this.emit('error', error);
|
|
reject(error);
|
|
reject(error);
|
|
});
|
|
});
|
|
@@ -345,17 +370,36 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Handle reconnection logic
|
|
|
|
|
|
+ * Handle reconnection logic with exponential backoff
|
|
*/
|
|
*/
|
|
private handleReconnect(): void {
|
|
private handleReconnect(): void {
|
|
|
|
+ // 如果连续失败次数过多,退出进程让外部重启
|
|
|
|
+ if (this.consecutiveFailures >= 5) {
|
|
|
|
+ this.logger.error(`🚨 连续失败${this.consecutiveFailures}次,退出进程等待外部重启`);
|
|
|
|
+ process.exit(1); // 退出进程,让外部supervisor重启
|
|
|
|
+ }
|
|
|
|
+
|
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
this.logger.error('Max reconnection attempts reached', { attempts: this.reconnectAttempts });
|
|
this.logger.error('Max reconnection attempts reached', { attempts: this.reconnectAttempts });
|
|
this.emit('maxReconnectAttemptsReached');
|
|
this.emit('maxReconnectAttemptsReached');
|
|
- return;
|
|
|
|
|
|
+ // 达到最大重连次数,退出进程
|
|
|
|
+ this.logger.error('🚨 达到最大重连次数,退出进程等待外部重启');
|
|
|
|
+ process.exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
this.reconnectAttempts++;
|
|
this.reconnectAttempts++;
|
|
- this.logger.info('Attempting to reconnect', { attempt: this.reconnectAttempts });
|
|
|
|
|
|
+
|
|
|
|
+ // 使用指数退避算法计算重连延迟
|
|
|
|
+ const baseDelay = this.config.reconnectInterval;
|
|
|
|
+ const maxDelay = 60000; // 最大延迟60秒
|
|
|
|
+ const exponentialDelay = Math.min(
|
|
|
|
+ baseDelay * Math.pow(1.5, this.reconnectAttempts - 1),
|
|
|
|
+ maxDelay
|
|
|
|
+ );
|
|
|
|
+ const jitter = Math.random() * 1000; // 添加0-1秒的随机抖动
|
|
|
|
+ const finalDelay = exponentialDelay + jitter;
|
|
|
|
+
|
|
|
|
+ this.logger.info(`⏳ 等待${(finalDelay/1000).toFixed(1)}秒后重连 (第${this.reconnectAttempts}/${this.config.maxReconnectAttempts}次)`);
|
|
|
|
|
|
this.reconnectTimer = setTimeout(async () => {
|
|
this.reconnectTimer = setTimeout(async () => {
|
|
try {
|
|
try {
|
|
@@ -366,7 +410,7 @@ export class PacificaWebSocketClient extends EventEmitter {
|
|
this.logger.error('Reconnection failed', { attempt: this.reconnectAttempts }, error as Error);
|
|
this.logger.error('Reconnection failed', { attempt: this.reconnectAttempts }, error as Error);
|
|
this.handleReconnect();
|
|
this.handleReconnect();
|
|
}
|
|
}
|
|
- }, this.config.reconnectInterval);
|
|
|
|
|
|
+ }, finalDelay);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|