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

552 lines
16 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, RpcErrorType } from '../types/RpcTypes';
/**
*
*/
interface DuplicateCallRecord {
callId: string;
methodName: string;
senderId: string;
firstCallTime: number;
lastCallTime: number;
callCount: number;
response?: RpcCallResponse;
}
/**
*
*/
interface IdempotencyConfig {
/** 是否启用幂等性检查 */
enabled: boolean;
/** 记录保留时间(毫秒) */
recordRetentionTime: number;
/** 最大记录数量 */
maxRecords: number;
/** 检查窗口时间(毫秒) */
checkWindowTime: number;
}
/**
*
*/
interface OrderedExecutionConfig {
/** 是否启用顺序执行 */
enabled: boolean;
/** 最大等待时间(毫秒) */
maxWaitTime: number;
/** 队列最大大小 */
maxQueueSize: number;
}
/**
*
*/
interface TransactionConfig {
/** 是否启用事务支持 */
enabled: boolean;
/** 事务超时时间(毫秒) */
transactionTimeout: number;
/** 最大事务数量 */
maxTransactions: number;
}
/**
* RPC可靠性管理器配置
*/
export interface RpcReliabilityConfig {
/** 幂等性配置 */
idempotency: IdempotencyConfig;
/** 顺序执行配置 */
orderedExecution: OrderedExecutionConfig;
/** 事务配置 */
transaction: TransactionConfig;
}
/**
*
*/
interface TransactionInfo {
transactionId: string;
calls: RpcCallRequest[];
startTime: number;
status: 'pending' | 'committed' | 'rolledback';
rollbackActions: Array<() => Promise<void>>;
}
/**
*
*/
interface OrderedQueueItem {
request: RpcCallRequest;
handler: () => Promise<RpcCallResponse>;
resolve: (response: RpcCallResponse) => void;
reject: (error: RpcError) => void;
enqueuedAt: number;
}
/**
* RPC可靠性管理器事件
*/
export interface RpcReliabilityManagerEvents {
duplicateCallDetected: (record: DuplicateCallRecord) => void;
transactionStarted: (transactionId: string) => void;
transactionCommitted: (transactionId: string) => void;
transactionRolledback: (transactionId: string, reason: string) => void;
orderedCallQueued: (callId: string, queueSize: number) => void;
orderedCallProcessed: (callId: string, waitTime: number) => void;
}
/**
* RPC可靠性管理器
*
*/
export class RpcReliabilityManager extends EventEmitter {
private logger = createLogger('RpcReliabilityManager');
private config: RpcReliabilityConfig;
/** 重复调用记录 */
private duplicateRecords = new Map<string, DuplicateCallRecord>();
/** 活跃事务 */
private transactions = new Map<string, TransactionInfo>();
/** 顺序执行队列(按发送者分组) */
private orderedQueues = new Map<string, OrderedQueueItem[]>();
/** 正在处理的有序调用 */
private processingOrdered = new Set<string>();
/** 清理定时器 */
private cleanupTimer: ReturnType<typeof setInterval> | null = null;
constructor(config: Partial<RpcReliabilityConfig> = {}) {
super();
this.config = {
idempotency: {
enabled: true,
recordRetentionTime: 300000, // 5分钟
maxRecords: 10000,
checkWindowTime: 60000, // 1分钟
...config.idempotency
},
orderedExecution: {
enabled: false,
maxWaitTime: 30000,
maxQueueSize: 1000,
...config.orderedExecution
},
transaction: {
enabled: false,
transactionTimeout: 60000,
maxTransactions: 100,
...config.transaction
}
};
this.startCleanupTimer();
}
/**
*
*/
public checkDuplicateCall(request: RpcCallRequest): {
isDuplicate: boolean;
response?: RpcCallResponse;
shouldProcess: boolean;
} {
if (!this.config.idempotency.enabled) {
return { isDuplicate: false, shouldProcess: true };
}
const key = `${request.senderId}:${request.callId}`;
const existing = this.duplicateRecords.get(key);
const now = Date.now();
if (existing) {
// 更新重复调用记录
existing.lastCallTime = now;
existing.callCount++;
this.emit('duplicateCallDetected', existing);
// 如果已有响应,直接返回
if (existing.response) {
return {
isDuplicate: true,
response: existing.response,
shouldProcess: false
};
}
// 如果在检查窗口内,认为是重复调用但还在处理中
if (now - existing.firstCallTime < this.config.idempotency.checkWindowTime) {
return {
isDuplicate: true,
shouldProcess: false
};
}
}
// 记录新的调用
const record: DuplicateCallRecord = {
callId: request.callId,
methodName: request.methodName,
senderId: request.senderId,
firstCallTime: now,
lastCallTime: now,
callCount: 1
};
this.duplicateRecords.set(key, record);
return { isDuplicate: false, shouldProcess: true };
}
/**
*
*/
public recordCallResponse(request: RpcCallRequest, response: RpcCallResponse): void {
if (!this.config.idempotency.enabled) {
return;
}
const key = `${request.senderId}:${request.callId}`;
const record = this.duplicateRecords.get(key);
if (record) {
record.response = response;
}
}
/**
*
*/
public async handleOrderedCall(
request: RpcCallRequest,
handler: () => Promise<RpcCallResponse>
): Promise<RpcCallResponse> {
if (!this.config.orderedExecution.enabled) {
return handler();
}
const senderId = request.senderId;
return new Promise<RpcCallResponse>((resolve, reject) => {
const queueItem: OrderedQueueItem = {
request,
handler,
resolve,
reject,
enqueuedAt: Date.now()
};
// 获取或创建队列
let queue = this.orderedQueues.get(senderId);
if (!queue) {
queue = [];
this.orderedQueues.set(senderId, queue);
}
// 检查队列大小
if (queue.length >= this.config.orderedExecution.maxQueueSize) {
reject({
type: RpcErrorType.RATE_LIMITED,
message: '有序执行队列已满'
});
return;
}
queue.push(queueItem);
this.emit('orderedCallQueued', request.callId, queue.length);
// 如果没有正在处理的调用,开始处理
if (!this.processingOrdered.has(senderId)) {
this.processOrderedQueue(senderId);
}
});
}
/**
*
*/
public startTransaction(transactionId: string): void {
if (!this.config.transaction.enabled) {
throw new Error('事务功能未启用');
}
if (this.transactions.has(transactionId)) {
throw new Error(`事务已存在: ${transactionId}`);
}
if (this.transactions.size >= this.config.transaction.maxTransactions) {
throw new Error('超过最大事务数量限制');
}
const transaction: TransactionInfo = {
transactionId,
calls: [],
startTime: Date.now(),
status: 'pending',
rollbackActions: []
};
this.transactions.set(transactionId, transaction);
this.emit('transactionStarted', transactionId);
// 设置事务超时
setTimeout(() => {
if (this.transactions.has(transactionId)) {
this.rollbackTransaction(transactionId, '事务超时');
}
}, this.config.transaction.transactionTimeout);
}
/**
*
*/
public addTransactionCall(
transactionId: string,
request: RpcCallRequest,
rollbackAction?: () => Promise<void>
): void {
const transaction = this.transactions.get(transactionId);
if (!transaction) {
throw new Error(`事务不存在: ${transactionId}`);
}
if (transaction.status !== 'pending') {
throw new Error(`事务状态无效: ${transaction.status}`);
}
transaction.calls.push(request);
if (rollbackAction) {
transaction.rollbackActions.push(rollbackAction);
}
}
/**
*
*/
public async commitTransaction(transactionId: string): Promise<void> {
const transaction = this.transactions.get(transactionId);
if (!transaction) {
throw new Error(`事务不存在: ${transactionId}`);
}
if (transaction.status !== 'pending') {
throw new Error(`事务状态无效: ${transaction.status}`);
}
transaction.status = 'committed';
this.transactions.delete(transactionId);
this.emit('transactionCommitted', transactionId);
this.logger.info(`事务已提交: ${transactionId},包含 ${transaction.calls.length} 个调用`);
}
/**
*
*/
public async rollbackTransaction(transactionId: string, reason: string): Promise<void> {
const transaction = this.transactions.get(transactionId);
if (!transaction) {
throw new Error(`事务不存在: ${transactionId}`);
}
if (transaction.status !== 'pending') {
return; // 已经处理过
}
transaction.status = 'rolledback';
// 执行回滚操作
for (const rollbackAction of transaction.rollbackActions.reverse()) {
try {
await rollbackAction();
} catch (error) {
this.logger.error(`回滚操作失败: ${transactionId}`, error);
}
}
this.transactions.delete(transactionId);
this.emit('transactionRolledback', transactionId, reason);
this.logger.warn(`事务已回滚: ${transactionId},原因: ${reason}`);
}
/**
*
*/
public getTransaction(transactionId: string): TransactionInfo | undefined {
return this.transactions.get(transactionId);
}
/**
*
*/
public getStats(): {
duplicateRecords: number;
activeTransactions: number;
totalQueuedCalls: number;
processingQueues: number;
} {
let totalQueuedCalls = 0;
for (const queue of this.orderedQueues.values()) {
totalQueuedCalls += queue.length;
}
return {
duplicateRecords: this.duplicateRecords.size,
activeTransactions: this.transactions.size,
totalQueuedCalls,
processingQueues: this.processingOrdered.size
};
}
/**
*
*/
public updateConfig(newConfig: Partial<RpcReliabilityConfig>): void {
if (newConfig.idempotency) {
Object.assign(this.config.idempotency, newConfig.idempotency);
}
if (newConfig.orderedExecution) {
Object.assign(this.config.orderedExecution, newConfig.orderedExecution);
}
if (newConfig.transaction) {
Object.assign(this.config.transaction, newConfig.transaction);
}
}
/**
*
*/
public destroy(): void {
// 停止清理定时器
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
// 回滚所有活跃事务
const transactionIds = Array.from(this.transactions.keys());
for (const transactionId of transactionIds) {
this.rollbackTransaction(transactionId, '管理器销毁').catch(error => {
this.logger.error(`销毁时回滚事务失败: ${transactionId}`, error);
});
}
// 清理队列
for (const queue of this.orderedQueues.values()) {
for (const item of queue) {
item.reject({
type: RpcErrorType.CLIENT_ERROR,
message: '服务关闭'
});
}
}
this.duplicateRecords.clear();
this.transactions.clear();
this.orderedQueues.clear();
this.processingOrdered.clear();
this.removeAllListeners();
}
/**
*
*/
private async processOrderedQueue(senderId: string): Promise<void> {
this.processingOrdered.add(senderId);
try {
const queue = this.orderedQueues.get(senderId);
if (!queue || queue.length === 0) {
return;
}
while (queue.length > 0) {
const item = queue.shift()!;
const waitTime = Date.now() - item.enqueuedAt;
// 检查等待时间是否超限
if (waitTime > this.config.orderedExecution.maxWaitTime) {
item.reject({
type: RpcErrorType.TIMEOUT,
message: `有序执行等待超时: ${waitTime}ms`
});
continue;
}
try {
const response = await item.handler();
item.resolve(response);
this.emit('orderedCallProcessed', item.request.callId, waitTime);
} catch (error) {
item.reject(error as RpcError);
}
}
} finally {
this.processingOrdered.delete(senderId);
// 如果队列还有新的项目,继续处理
const queue = this.orderedQueues.get(senderId);
if (queue && queue.length > 0) {
setImmediate(() => this.processOrderedQueue(senderId));
}
}
}
/**
*
*/
private startCleanupTimer(): void {
this.cleanupTimer = setInterval(() => {
this.cleanup();
}, 60000); // 每分钟清理一次
}
/**
*
*/
private cleanup(): void {
const now = Date.now();
// 清理过期的重复调用记录
if (this.config.idempotency.enabled) {
for (const [key, record] of this.duplicateRecords) {
if (now - record.lastCallTime > this.config.idempotency.recordRetentionTime) {
this.duplicateRecords.delete(key);
}
}
// 限制记录数量
if (this.duplicateRecords.size > this.config.idempotency.maxRecords) {
const sortedRecords = Array.from(this.duplicateRecords.entries())
.sort(([,a], [,b]) => a.lastCallTime - b.lastCallTime);
const keepCount = Math.floor(this.config.idempotency.maxRecords * 0.8);
for (let i = 0; i < sortedRecords.length - keepCount; i++) {
this.duplicateRecords.delete(sortedRecords[i][0]);
}
}
}
// 清理空的有序队列
for (const [senderId, queue] of this.orderedQueues) {
if (queue.length === 0 && !this.processingOrdered.has(senderId)) {
this.orderedQueues.delete(senderId);
}
}
}
}