Files
esengine/packages/network-shared/src/rpc/RpcCallProxy.ts

505 lines
14 KiB
TypeScript
Raw Normal View History

2025-08-20 10:32:56 +08:00
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
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-08-20 10:32:56 +08:00
/** 待处理的调用 */
private pendingCalls = new Map<string, RpcCallInfo>();
2025-08-20 10:32:56 +08:00
/** 离线队列 */
private offlineQueue: RpcCallRequest[] = [];
2025-08-20 10:32:56 +08:00
/** 是否在线 */
private isOnline = true;
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-08-20 10:32:56 +08:00
/** 重试定时器 */
private retryTimers = new Map<string, ReturnType<typeof setTimeout>>();
constructor(
networkSender: NetworkSender,
config: Partial<RpcCallProxyConfig> = {}
) {
super();
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-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-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-08-20 10:32:56 +08:00
this.pendingCalls.set(callId, callInfo);
this.stats.pendingCalls++;
this.stats.totalCalls++;
2025-08-20 10:32:56 +08:00
// 设置超时
setTimeout(() => {
this.handleTimeout(callId);
}, timeout);
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-08-20 10:32:56 +08:00
// 清理定时器
const timer = this.retryTimers.get(response.callId);
if (timer) {
clearTimeout(timer);
this.retryTimers.delete(response.callId);
}
2025-08-20 10:32:56 +08:00
// 更新状态
callInfo.status = response.success ? RpcCallStatus.COMPLETED : RpcCallStatus.FAILED;
callInfo.completedAt = Date.now();
2025-08-20 10:32:56 +08:00
// 更新统计
if (response.success) {
this.stats.successfulCalls++;
this.updateAverageResponseTime(response.duration);
} else {
this.stats.failedCalls++;
}
2025-08-20 10:32:56 +08:00
this.stats.pendingCalls--;
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-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-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-08-20 10:32:56 +08:00
// 清理定时器
const timer = this.retryTimers.get(callId);
if (timer) {
clearTimeout(timer);
this.retryTimers.delete(callId);
}
2025-08-20 10:32:56 +08:00
// 更新状态
callInfo.status = RpcCallStatus.CANCELLED;
2025-08-20 10:32:56 +08:00
// 拒绝Promise
callInfo.reject!({
type: RpcErrorType.CLIENT_ERROR,
message: '调用被取消'
});
2025-08-20 10:32:56 +08:00
// 清理
this.pendingCalls.delete(callId);
this.stats.pendingCalls--;
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-08-20 10:32:56 +08:00
// 清理定时器
for (const timer of this.retryTimers.values()) {
clearTimeout(timer);
}
this.retryTimers.clear();
2025-08-20 10:32:56 +08:00
// 清理队列
this.offlineQueue.length = 0;
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-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-08-20 10:32:56 +08:00
// 发送消息
await this.networkSender.sendMessage(message);
2025-08-20 10:32:56 +08:00
callInfo.status = RpcCallStatus.SENT;
callInfo.sentAt = Date.now();
2025-08-20 10:32:56 +08:00
this.emit('callSent', callInfo.request);
2025-08-20 10:32:56 +08:00
} catch (error) {
this.logger.error(`发送RPC调用失败: ${callInfo.request.methodName}`, error);
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-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-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-08-20 10:32:56 +08:00
callInfo.nextRetryTime = Date.now() + delay;
2025-08-20 10:32:56 +08:00
this.emit('retryAttempt', callInfo.request.callId, callInfo.retryCount);
2025-08-20 10:32:56 +08:00
const timer = setTimeout(() => {
this.retryTimers.delete(callInfo.request.callId);
this.sendCall(callInfo);
}, delay);
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-08-20 10:32:56 +08:00
callInfo.reject!(error);
2025-08-20 10:32:56 +08:00
this.pendingCalls.delete(callInfo.request.callId);
this.stats.pendingCalls--;
this.stats.failedCalls++;
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-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-08-20 10:32:56 +08:00
const queue = [...this.offlineQueue];
this.offlineQueue.length = 0;
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-08-20 10:32:56 +08:00
this.pendingCalls.set(request.callId, callInfo);
await this.sendCall(callInfo);
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;
this.stats.averageResponseTime =
2025-08-20 10:32:56 +08:00
(currentAverage * (totalResponses - 1) + responseTime) / totalResponses;
}
}