使用setTimeout、setInterval与回调实现客户端心跳检测求助
搞定RecordTracker类的ping/pong客户端追踪逻辑
嘿,我来帮你一步步完善这个RecordTracker类,顺便解决你纠结的模拟客户端问题!你的核心需求很清晰:每隔10秒给客户端发ping,3秒没收到pong就踢掉,咱们拆解开来实现。
核心思路梳理
首先得明确几个关键步骤:
- 每10秒遍历所有客户端,发送
ping消息 - 给每个发送了ping的客户端设置3秒超时器
- 监听客户端的
pong回复,收到后取消对应超时器 - 超时未回复的客户端,从
recordMap中移除 - 需要一个临时存储来跟踪等待pong的客户端(避免重复处理或误删)
完善RecordTracker类
我们需要给类加一个状态来跟踪pending的pong请求,同时完善initialize里的逻辑:
interface Client { sendMessage: (message: string) => void; onMessage: (callback: (message: string) => void) => void; } type RecordMap = { [recordName: string]: Client }; class RecordTracker { // 用来存储等待pong的客户端名和对应的超时ID private pendingPongs: Map<string, NodeJS.Timeout> = new Map(); constructor(private recordMap: RecordMap) {} public initialize(): void { // 每10秒执行一轮检测 setInterval(() => { // 遍历当前所有客户端 Object.entries(this.recordMap).forEach(([clientName, client]) => { // 发送ping消息 client.sendMessage('ping'); // 设置3秒超时:如果没收到pong就移除客户端 const timeoutId = setTimeout(() => { if (this.pendingPongs.has(clientName)) { // 移除未回复的客户端 delete this.recordMap[clientName]; this.pendingPongs.delete(clientName); console.log(`客户端 ${clientName} 未回复pong,已移除`); } }, 3000); // 把超时ID存入pendingPongs this.pendingPongs.set(clientName, timeoutId); // 监听客户端的pong回复 client.onMessage((message) => { if (message === 'pong' && this.pendingPongs.has(clientName)) { // 收到pong,清除超时器 clearTimeout(this.pendingPongs.get(clientName)!); this.pendingPongs.delete(clientName); console.log(`客户端 ${clientName} 已回复pong`); } }); }); }, 10000); } public getRecordMap(): RecordMap { return { ...this.recordMap }; } }
为什么需要模拟客户端?
当然需要!没有模拟客户端的话,你没法验证代码是否真的能发送ping、监听pong、超时移除这些逻辑。咱们写一个MockClient来模拟真实的客户端行为:
class MockClient implements Client { private messageCallbacks: ((message: string) => void)[] = []; public sentMessages: string[] = []; // 用来记录发送的消息,方便测试 sendMessage(message: string): void { this.sentMessages.push(message); console.log(`Mock客户端发送消息:${message}`); } onMessage(callback: (message: string) => void): void { this.messageCallbacks.push(callback); } // 手动触发pong回复,模拟客户端收到ping后回复 triggerPong(): void { this.messageCallbacks.forEach(cb => cb('pong')); } }
测试你的逻辑
现在可以用MockClient来测试RecordTracker的功能了:
// 创建两个模拟客户端 const client1 = new MockClient(); const client2 = new MockClient(); // 初始化RecordTracker const tracker = new RecordTracker({ clientA: client1, clientB: client2 }); // 启动追踪 tracker.initialize(); // 模拟client1回复pong,client2不回复 setTimeout(() => { client1.triggerPong(); }, 2000); // 15秒后查看剩余客户端(此时已经过了一轮10秒检测+3秒超时) setTimeout(() => { console.log('剩余客户端:', Object.keys(tracker.getRecordMap())); // 应该只剩下clientA }, 15000);
一些注意事项
- 内存泄漏问题:如果客户端频繁加入或移除,要确保
pendingPongs里的超时器和回调都被正确清理,避免内存泄漏。 - 重复监听问题:上面的代码每次轮询都会给客户端绑定
onMessage回调,可能会导致同一个客户端有多个回调。可以优化成给每个客户端只绑定一次回调,或者在轮询前先清除旧的回调(取决于你的业务场景)。 - 线程安全:如果是在多线程环境下,可能需要加锁,但Node.js是单线程的,所以暂时不用考虑。
这样你的RecordTracker就完全实现需求啦,模拟客户端也能帮你验证所有逻辑是否正常工作!
内容的提问来源于stack exchange,提问作者bootsElectric
相关产品推荐
相关产品推荐

