2025-08-20 10:32:56 +08:00
|
|
|
|
import { createLogger } from '@esengine/ecs-framework';
|
|
|
|
|
|
import { EventEmitter } from '../utils/EventEmitter';
|
2025-11-02 23:50:41 +08:00
|
|
|
|
import {
|
|
|
|
|
|
RpcCallRequest,
|
|
|
|
|
|
RpcCallResponse,
|
|
|
|
|
|
RpcError,
|
2025-08-20 10:32:56 +08:00
|
|
|
|
RpcErrorType,
|
|
|
|
|
|
RpcCallInfo,
|
|
|
|
|
|
RpcCallStatus,
|
|
|
|
|
|
RpcStats,
|
|
|
|
|
|
RpcOptions,
|
|
|
|
|
|
ClientRpcInvoker,
|
|
|
|
|
|
ServerRpcInvoker
|
|
|
|
|
|
} from '../types/RpcTypes';
|
|
|
|
|
|
import { MessageType, RpcTarget } from '../types/NetworkTypes';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 网络发送器接口
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface NetworkSender {
|
|
|
|
|
|
sendMessage(message: object): Promise<void>;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* RPC调用代理事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface RpcCallProxyEvents {
|
|
|
|
|
|
callSent: (request: RpcCallRequest) => void;
|
|
|
|
|
|
responseReceived: (response: RpcCallResponse) => void;
|
|
|
|
|
|
callTimeout: (callId: string) => void;
|
|
|
|
|
|
callFailed: (callId: string, error: RpcError) => void;
|
|
|
|
|
|
retryAttempt: (callId: string, attempt: number) => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* RPC调用代理配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface RpcCallProxyConfig {
|
|
|
|
|
|
/** 默认超时时间(毫秒) */
|
|
|
|
|
|
defaultTimeout: number;
|
|
|
|
|
|
/** 最大重试次数 */
|
|
|
|
|
|
maxRetries: number;
|
|
|
|
|
|
/** 重试延迟基数(毫秒) */
|
|
|
|
|
|
retryDelayBase: number;
|
|
|
|
|
|
/** 重试延迟倍数 */
|
|
|
|
|
|
retryDelayMultiplier: number;
|
|
|
|
|
|
/** 最大重试延迟(毫秒) */
|
|
|
|
|
|
maxRetryDelay: number;
|
|
|
|
|
|
/** 是否启用离线队列 */
|
|
|
|
|
|
enableOfflineQueue: boolean;
|
|
|
|
|
|
/** 离线队列最大大小 */
|
|
|
|
|
|
maxOfflineQueueSize: number;
|
|
|
|
|
|
/** 调用ID生成器 */
|
|
|
|
|
|
generateCallId?: () => string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* RPC调用代理
|
|
|
|
|
|
* 负责发送RPC调用并处理响应
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class RpcCallProxy extends EventEmitter {
|
|
|
|
|
|
private logger = createLogger('RpcCallProxy');
|
|
|
|
|
|
private config: RpcCallProxyConfig;
|
|
|
|
|
|
private networkSender: NetworkSender;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
/** 待处理的调用 */
|
|
|
|
|
|
private pendingCalls = new Map<string, RpcCallInfo>();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
/** 离线队列 */
|
|
|
|
|
|
private offlineQueue: RpcCallRequest[] = [];
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
/** 是否在线 */
|
|
|
|
|
|
private isOnline = true;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
/** 统计信息 */
|
|
|
|
|
|
private stats: RpcStats = {
|
|
|
|
|
|
totalCalls: 0,
|
|
|
|
|
|
successfulCalls: 0,
|
|
|
|
|
|
failedCalls: 0,
|
|
|
|
|
|
averageResponseTime: 0,
|
|
|
|
|
|
pendingCalls: 0,
|
|
|
|
|
|
timeoutCalls: 0,
|
|
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
lastUpdated: Date.now()
|
|
|
|
|
|
};
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
/** 重试定时器 */
|
|
|
|
|
|
private retryTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
|
networkSender: NetworkSender,
|
|
|
|
|
|
config: Partial<RpcCallProxyConfig> = {}
|
|
|
|
|
|
) {
|
|
|
|
|
|
super();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.networkSender = networkSender;
|
|
|
|
|
|
this.config = {
|
|
|
|
|
|
defaultTimeout: 30000,
|
|
|
|
|
|
maxRetries: 3,
|
|
|
|
|
|
retryDelayBase: 1000,
|
|
|
|
|
|
retryDelayMultiplier: 2,
|
|
|
|
|
|
maxRetryDelay: 10000,
|
|
|
|
|
|
enableOfflineQueue: true,
|
|
|
|
|
|
maxOfflineQueueSize: 100,
|
|
|
|
|
|
generateCallId: () => `rpc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
|
|
|
|
...config
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 客户端RPC调用器
|
|
|
|
|
|
*/
|
|
|
|
|
|
public get clientRpc(): ClientRpcInvoker {
|
|
|
|
|
|
return <TArgs extends readonly unknown[], TReturn>(
|
|
|
|
|
|
methodName: string,
|
|
|
|
|
|
args: TArgs,
|
|
|
|
|
|
options?: Partial<RpcOptions>
|
|
|
|
|
|
): Promise<TReturn> => {
|
|
|
|
|
|
return this.call(methodName, args, options);
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 服务端RPC调用器(用于服务端调用客户端)
|
|
|
|
|
|
*/
|
|
|
|
|
|
public get serverRpc(): ServerRpcInvoker {
|
|
|
|
|
|
return <TArgs extends readonly unknown[], TReturn>(
|
|
|
|
|
|
clientId: string,
|
|
|
|
|
|
methodName: string,
|
|
|
|
|
|
args: TArgs,
|
|
|
|
|
|
options?: Partial<RpcOptions>
|
|
|
|
|
|
): Promise<TReturn> => {
|
|
|
|
|
|
const callOptions = {
|
|
|
|
|
|
...options,
|
|
|
|
|
|
target: RpcTarget.Client
|
|
|
|
|
|
};
|
|
|
|
|
|
return this.call(methodName, args, callOptions, clientId);
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发起RPC调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
public async call<TArgs extends readonly unknown[], TReturn>(
|
|
|
|
|
|
methodName: string,
|
|
|
|
|
|
args: TArgs,
|
|
|
|
|
|
options: Partial<RpcOptions> = {},
|
|
|
|
|
|
targetId?: string
|
|
|
|
|
|
): Promise<TReturn> {
|
|
|
|
|
|
const callId = this.config.generateCallId!();
|
|
|
|
|
|
const timeout = options.timeout || this.config.defaultTimeout;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
const request: RpcCallRequest<TArgs> = {
|
|
|
|
|
|
callId,
|
|
|
|
|
|
methodName,
|
|
|
|
|
|
args,
|
|
|
|
|
|
senderId: 'client', // 这应该从认证系统获取
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
options: {
|
|
|
|
|
|
reliable: true,
|
|
|
|
|
|
priority: 5,
|
|
|
|
|
|
timeout,
|
|
|
|
|
|
...options
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 创建Promise和调用信息
|
|
|
|
|
|
return new Promise<TReturn>((resolve, reject) => {
|
|
|
|
|
|
const callInfo: RpcCallInfo<TArgs> = {
|
|
|
|
|
|
request,
|
|
|
|
|
|
status: RpcCallStatus.PENDING,
|
|
|
|
|
|
resolve: resolve as (value: unknown) => void,
|
|
|
|
|
|
reject: (reason: RpcError) => reject(reason),
|
|
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
createdAt: Date.now()
|
|
|
|
|
|
};
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.pendingCalls.set(callId, callInfo);
|
|
|
|
|
|
this.stats.pendingCalls++;
|
|
|
|
|
|
this.stats.totalCalls++;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 设置超时
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.handleTimeout(callId);
|
|
|
|
|
|
}, timeout);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 发送调用
|
|
|
|
|
|
this.sendCall(callInfo);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理RPC响应
|
|
|
|
|
|
*/
|
|
|
|
|
|
public handleResponse(response: RpcCallResponse): void {
|
|
|
|
|
|
const callInfo = this.pendingCalls.get(response.callId);
|
|
|
|
|
|
if (!callInfo) {
|
|
|
|
|
|
this.logger.warn(`收到未知调用的响应: ${response.callId}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理定时器
|
|
|
|
|
|
const timer = this.retryTimers.get(response.callId);
|
|
|
|
|
|
if (timer) {
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
this.retryTimers.delete(response.callId);
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 更新状态
|
|
|
|
|
|
callInfo.status = response.success ? RpcCallStatus.COMPLETED : RpcCallStatus.FAILED;
|
|
|
|
|
|
callInfo.completedAt = Date.now();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 更新统计
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
this.stats.successfulCalls++;
|
|
|
|
|
|
this.updateAverageResponseTime(response.duration);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.stats.failedCalls++;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.stats.pendingCalls--;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 处理结果
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
callInfo.resolve!(response.result);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
callInfo.reject!(response.error!);
|
|
|
|
|
|
this.emit('callFailed', response.callId, response.error!);
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理
|
|
|
|
|
|
this.pendingCalls.delete(response.callId);
|
|
|
|
|
|
this.emit('responseReceived', response);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置网络状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
public setOnlineStatus(online: boolean): void {
|
|
|
|
|
|
const wasOnline = this.isOnline;
|
|
|
|
|
|
this.isOnline = online;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
if (online && !wasOnline) {
|
|
|
|
|
|
// 从离线状态恢复,处理离线队列
|
|
|
|
|
|
this.processOfflineQueue();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消RPC调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
public cancelCall(callId: string): boolean {
|
|
|
|
|
|
const callInfo = this.pendingCalls.get(callId);
|
|
|
|
|
|
if (!callInfo) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理定时器
|
|
|
|
|
|
const timer = this.retryTimers.get(callId);
|
|
|
|
|
|
if (timer) {
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
this.retryTimers.delete(callId);
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 更新状态
|
|
|
|
|
|
callInfo.status = RpcCallStatus.CANCELLED;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 拒绝Promise
|
|
|
|
|
|
callInfo.reject!({
|
|
|
|
|
|
type: RpcErrorType.CLIENT_ERROR,
|
|
|
|
|
|
message: '调用被取消'
|
|
|
|
|
|
});
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理
|
|
|
|
|
|
this.pendingCalls.delete(callId);
|
|
|
|
|
|
this.stats.pendingCalls--;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取统计信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getStats(): RpcStats {
|
|
|
|
|
|
this.stats.lastUpdated = Date.now();
|
|
|
|
|
|
return { ...this.stats };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取待处理的调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getPendingCalls(): RpcCallInfo[] {
|
|
|
|
|
|
return Array.from(this.pendingCalls.values());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重置统计信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public resetStats(): void {
|
|
|
|
|
|
this.stats = {
|
|
|
|
|
|
totalCalls: 0,
|
|
|
|
|
|
successfulCalls: 0,
|
|
|
|
|
|
failedCalls: 0,
|
|
|
|
|
|
averageResponseTime: 0,
|
|
|
|
|
|
pendingCalls: this.pendingCalls.size,
|
|
|
|
|
|
timeoutCalls: 0,
|
|
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
lastUpdated: Date.now()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
public updateConfig(newConfig: Partial<RpcCallProxyConfig>): void {
|
|
|
|
|
|
Object.assign(this.config, newConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 销毁代理
|
|
|
|
|
|
*/
|
|
|
|
|
|
public destroy(): void {
|
|
|
|
|
|
// 取消所有待处理的调用
|
|
|
|
|
|
const pendingCallIds = Array.from(this.pendingCalls.keys());
|
|
|
|
|
|
for (const callId of pendingCallIds) {
|
|
|
|
|
|
this.cancelCall(callId);
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理定时器
|
|
|
|
|
|
for (const timer of this.retryTimers.values()) {
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.retryTimers.clear();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 清理队列
|
|
|
|
|
|
this.offlineQueue.length = 0;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.removeAllListeners();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发送调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
private async sendCall<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>): Promise<void> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 检查网络状态
|
|
|
|
|
|
if (!this.isOnline) {
|
|
|
|
|
|
if (this.config.enableOfflineQueue) {
|
|
|
|
|
|
this.addToOfflineQueue(callInfo.request);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error('网络不可用');
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 构建网络消息
|
|
|
|
|
|
const message = {
|
|
|
|
|
|
type: MessageType.RPC_CALL,
|
|
|
|
|
|
messageId: callInfo.request.callId,
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
senderId: callInfo.request.senderId,
|
|
|
|
|
|
data: callInfo.request,
|
|
|
|
|
|
reliable: callInfo.request.options.reliable,
|
|
|
|
|
|
priority: callInfo.request.options.priority
|
|
|
|
|
|
};
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 发送消息
|
|
|
|
|
|
await this.networkSender.sendMessage(message);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
callInfo.status = RpcCallStatus.SENT;
|
|
|
|
|
|
callInfo.sentAt = Date.now();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.emit('callSent', callInfo.request);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
this.logger.error(`发送RPC调用失败: ${callInfo.request.methodName}`, error);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 检查是否可以重试
|
|
|
|
|
|
if (callInfo.retryCount < this.config.maxRetries) {
|
|
|
|
|
|
this.scheduleRetry(callInfo);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.handleCallFailure(callInfo, {
|
|
|
|
|
|
type: RpcErrorType.NETWORK_ERROR,
|
|
|
|
|
|
message: String(error)
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理超时
|
|
|
|
|
|
*/
|
|
|
|
|
|
private handleTimeout(callId: string): void {
|
|
|
|
|
|
const callInfo = this.pendingCalls.get(callId);
|
|
|
|
|
|
if (!callInfo || callInfo.status === RpcCallStatus.COMPLETED) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 检查是否可以重试
|
|
|
|
|
|
if (callInfo.retryCount < this.config.maxRetries) {
|
|
|
|
|
|
this.scheduleRetry(callInfo);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.stats.timeoutCalls++;
|
|
|
|
|
|
this.handleCallFailure(callInfo, {
|
|
|
|
|
|
type: RpcErrorType.TIMEOUT,
|
|
|
|
|
|
message: `调用超时: ${callInfo.request.options.timeout}ms`
|
|
|
|
|
|
});
|
|
|
|
|
|
this.emit('callTimeout', callId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 安排重试
|
|
|
|
|
|
*/
|
|
|
|
|
|
private scheduleRetry<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>): void {
|
|
|
|
|
|
callInfo.retryCount++;
|
|
|
|
|
|
this.stats.retryCount++;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
// 计算延迟时间(指数退避)
|
|
|
|
|
|
const baseDelay = this.config.retryDelayBase * Math.pow(this.config.retryDelayMultiplier, callInfo.retryCount - 1);
|
|
|
|
|
|
const delay = Math.min(baseDelay, this.config.maxRetryDelay);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
callInfo.nextRetryTime = Date.now() + delay;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.emit('retryAttempt', callInfo.request.callId, callInfo.retryCount);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
|
this.retryTimers.delete(callInfo.request.callId);
|
|
|
|
|
|
this.sendCall(callInfo);
|
|
|
|
|
|
}, delay);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.retryTimers.set(callInfo.request.callId, timer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理调用失败
|
|
|
|
|
|
*/
|
|
|
|
|
|
private handleCallFailure<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>, error: RpcError): void {
|
|
|
|
|
|
callInfo.status = RpcCallStatus.FAILED;
|
|
|
|
|
|
callInfo.completedAt = Date.now();
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
callInfo.reject!(error);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.pendingCalls.delete(callInfo.request.callId);
|
|
|
|
|
|
this.stats.pendingCalls--;
|
|
|
|
|
|
this.stats.failedCalls++;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.emit('callFailed', callInfo.request.callId, error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加到离线队列
|
|
|
|
|
|
*/
|
|
|
|
|
|
private addToOfflineQueue<T extends readonly unknown[]>(request: RpcCallRequest<T>): void {
|
|
|
|
|
|
if (this.offlineQueue.length >= this.config.maxOfflineQueueSize) {
|
|
|
|
|
|
// 移除最旧的请求
|
|
|
|
|
|
this.offlineQueue.shift();
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.offlineQueue.push(request);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理离线队列
|
|
|
|
|
|
*/
|
|
|
|
|
|
private async processOfflineQueue(): Promise<void> {
|
|
|
|
|
|
if (!this.isOnline || this.offlineQueue.length === 0) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
const queue = [...this.offlineQueue];
|
|
|
|
|
|
this.offlineQueue.length = 0;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
for (const request of queue) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 重新创建调用信息
|
|
|
|
|
|
const callInfo: RpcCallInfo = {
|
|
|
|
|
|
request,
|
|
|
|
|
|
status: RpcCallStatus.PENDING,
|
|
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
createdAt: Date.now()
|
|
|
|
|
|
};
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
this.pendingCalls.set(request.callId, callInfo);
|
|
|
|
|
|
await this.sendCall(callInfo);
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
2025-08-20 10:32:56 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
this.logger.error(`处理离线队列失败: ${request.methodName}`, error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新平均响应时间
|
|
|
|
|
|
*/
|
|
|
|
|
|
private updateAverageResponseTime(responseTime: number): void {
|
|
|
|
|
|
const totalResponses = this.stats.successfulCalls;
|
|
|
|
|
|
const currentAverage = this.stats.averageResponseTime;
|
2025-11-02 23:50:41 +08:00
|
|
|
|
|
|
|
|
|
|
this.stats.averageResponseTime =
|
2025-08-20 10:32:56 +08:00
|
|
|
|
(currentAverage * (totalResponses - 1) + responseTime) / totalResponses;
|
|
|
|
|
|
}
|
2025-11-02 23:50:41 +08:00
|
|
|
|
}
|