feat: 添加自动重连机制和重连配置选项到 RemoteApp 类,并更新相关文档
This commit is contained in:
@@ -6,6 +6,16 @@ type RemoteAppOptions = {
|
||||
token?: string;
|
||||
emitter?: EventEmitter;
|
||||
id?: string;
|
||||
/** 是否启用自动重连,默认 true */
|
||||
autoReconnect?: boolean;
|
||||
/** 最大重连次数,默认 Infinity */
|
||||
maxReconnectAttempts?: number;
|
||||
/** 初始重连延迟(毫秒),默认 1000 */
|
||||
reconnectDelay?: number;
|
||||
/** 重连延迟最大值(毫秒),默认 30000 */
|
||||
maxReconnectDelay?: number;
|
||||
/** 是否启用指数退避,默认 true */
|
||||
enableBackoff?: boolean;
|
||||
};
|
||||
/**
|
||||
* 远程共享地址类似:https://kevisual.cn/ws/proxy
|
||||
@@ -19,6 +29,15 @@ export class RemoteApp {
|
||||
ws: WebSocket;
|
||||
remoteIsConnected: boolean;
|
||||
isError: boolean = false;
|
||||
// 重连相关属性
|
||||
autoReconnect: boolean;
|
||||
maxReconnectAttempts: number;
|
||||
reconnectDelay: number;
|
||||
maxReconnectDelay: number;
|
||||
enableBackoff: boolean;
|
||||
reconnectAttempts: number = 0;
|
||||
reconnectTimer: NodeJS.Timeout | null = null;
|
||||
isManuallyClosed: boolean = false;
|
||||
constructor(opts?: RemoteAppOptions) {
|
||||
this.mainApp = opts?.app;
|
||||
const token = opts.token;
|
||||
@@ -32,6 +51,12 @@ export class RemoteApp {
|
||||
_url.searchParams.set('id', id);
|
||||
this.url = _url.toString();
|
||||
this.id = id;
|
||||
// 初始化重连相关配置
|
||||
this.autoReconnect = opts?.autoReconnect ?? true;
|
||||
this.maxReconnectAttempts = opts?.maxReconnectAttempts ?? Infinity;
|
||||
this.reconnectDelay = opts?.reconnectDelay ?? 1000;
|
||||
this.maxReconnectDelay = opts?.maxReconnectDelay ?? 30000;
|
||||
this.enableBackoff = opts?.enableBackoff ?? true;
|
||||
this.init();
|
||||
}
|
||||
async isConnect(): Promise<boolean> {
|
||||
@@ -39,6 +64,11 @@ export class RemoteApp {
|
||||
if (this.isConnected) {
|
||||
return true;
|
||||
}
|
||||
// 如果正在进行重连,等待连接成功
|
||||
if (this.reconnectTimer !== null) {
|
||||
console.log(`远程应用 ${this.id} 正在重连中...`);
|
||||
}
|
||||
// 等待连接成功(支持初次连接和重连场景)
|
||||
return new Promise((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
resolve(false);
|
||||
@@ -90,12 +120,84 @@ export class RemoteApp {
|
||||
this.ws = ws;
|
||||
}
|
||||
onOpen() {
|
||||
this.isError = false;
|
||||
this.reconnectAttempts = 0;
|
||||
// 清除可能存在的重连定时器
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
this.reconnectTimer = null;
|
||||
}
|
||||
this.emitter.emit('open', this.id);
|
||||
}
|
||||
onClose() {
|
||||
console.log('远程应用关闭:', this.id);
|
||||
this.emitter.emit('close', this.id);
|
||||
this.isConnected = false;
|
||||
this.emitter.emit('close', this.id);
|
||||
// 触发自动重连逻辑
|
||||
if (this.autoReconnect && !this.isManuallyClosed) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
}
|
||||
/** 计算下一次重连延迟 */
|
||||
calculateReconnectDelay(): number {
|
||||
if (!this.enableBackoff) {
|
||||
return this.reconnectDelay;
|
||||
}
|
||||
// 指数退避算法:delay = initialDelay * 2^(attempts - 1)
|
||||
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
|
||||
return Math.min(delay, this.maxReconnectDelay);
|
||||
}
|
||||
/** 安排重连 */
|
||||
scheduleReconnect() {
|
||||
// 检查是否达到最大重连次数
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
console.error(`远程应用 ${this.id} 已达到最大重连次数 ${this.maxReconnectAttempts},停止重连`);
|
||||
this.emitter.emit('maxReconnectAttemptsReached', this.id);
|
||||
return;
|
||||
}
|
||||
// 清除可能存在的定时器
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
}
|
||||
const delay = this.calculateReconnectDelay();
|
||||
this.reconnectAttempts++;
|
||||
console.log(`远程应用 ${this.id} 将在 ${delay}ms 后尝试第 ${this.reconnectAttempts} 次重连`);
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.reconnectTimer = null;
|
||||
try {
|
||||
this.init();
|
||||
} catch (error) {
|
||||
console.error(`远程应用 ${this.id} 重连失败:`, error);
|
||||
this.emitter.emit('reconnectFailed', { id: this.id, attempt: this.reconnectAttempts, error });
|
||||
// 重连失败后继续尝试重连
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
/** 手动关闭连接,停止自动重连 */
|
||||
disconnect() {
|
||||
this.isManuallyClosed = true;
|
||||
this.autoReconnect = false;
|
||||
// 清除重连定时器
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
this.reconnectTimer = null;
|
||||
}
|
||||
// 关闭 WebSocket
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
}
|
||||
}
|
||||
/** 手动重连 */
|
||||
reconnect() {
|
||||
this.isManuallyClosed = false;
|
||||
this.reconnectAttempts = 0;
|
||||
// 清除可能存在的定时器
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
this.reconnectTimer = null;
|
||||
}
|
||||
this.init();
|
||||
}
|
||||
onMessage(data: any) {
|
||||
this.emitter.emit('message', data);
|
||||
@@ -105,7 +207,7 @@ export class RemoteApp {
|
||||
this.isError = true;
|
||||
this.emitter.emit('error', error);
|
||||
}
|
||||
on(event: 'open' | 'close' | 'message' | 'error', listener: (data: any) => void) {
|
||||
on(event: 'open' | 'close' | 'message' | 'error' | 'maxReconnectAttemptsReached' | 'reconnectFailed', listener: (data: any) => void) {
|
||||
this.emitter.on(event, listener);
|
||||
return () => {
|
||||
this.emitter.off(event, listener);
|
||||
|
||||
Reference in New Issue
Block a user