diff --git a/lib/server/timer-manager.d.ts b/lib/server/timer-manager.d.ts new file mode 100644 index 0000000..4714d23 --- /dev/null +++ b/lib/server/timer-manager.d.ts @@ -0,0 +1,45 @@ +type TimerType = 'timeout' | 'interval' | 'immediate'; +interface TimerRecord { + id: NodeJS.Timeout | NodeJS.Immediate; + type: TimerType; + createdAt: number; +} +declare class GlobalTimerManager { + private timers; + private isHooked; + private readonly original; + /** + * 开始拦截全局定时器 + */ + hook(): void; + /** + * 恢复原始的全局定时器函数 + */ + unhook(): void; + /** + * 清除所有被追踪的定时器 + */ + clearAll(): number; + /** + * 按类型清除定时器 + */ + clearByType(type: TimerType): number; + /** + * 获取当前活跃的定时器数量 + */ + getActiveCount(): number; + /** + * 获取定时器统计信息 + */ + getStats(): Record; + /** + * 获取所有定时器的详细信息(用于调试) + */ + getTimers(): TimerRecord[]; +} +export declare const timerManager: GlobalTimerManager; +export declare const hookTimers: () => void; +export declare const unhookTimers: () => void; +export declare const clearAllTimers: () => number; +export declare const getTimerStats: () => Record; +export {}; diff --git a/lib/server/timer-manager.js b/lib/server/timer-manager.js new file mode 100644 index 0000000..c8dfa5e --- /dev/null +++ b/lib/server/timer-manager.js @@ -0,0 +1,162 @@ +"use strict"; +// timer-manager.ts +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getTimerStats = exports.clearAllTimers = exports.unhookTimers = exports.hookTimers = exports.timerManager = void 0; +class GlobalTimerManager { + timers = new Map(); + isHooked = false; + // 保存原始函数 + original = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + setImmediate: global.setImmediate, clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval, + clearImmediate: global.clearImmediate, + }; + /** + * 开始拦截全局定时器 + */ + hook() { + if (this.isHooked) { + console.warn('[TimerManager] Already hooked'); + return; + } + const self = this; + // Hook setTimeout + global.setTimeout = function (callback, ms, ...args) { + const id = self.original.setTimeout((...callbackArgs) => { + self.timers.delete(id); // 执行完后移除 + callback(...callbackArgs); + }, ms, ...args); + self.timers.set(id, { + id, + type: 'timeout', + createdAt: Date.now(), + }); + return id; + }; + // Hook setInterval + global.setInterval = function (callback, ms, ...args) { + const id = self.original.setInterval(callback, ms, ...args); + self.timers.set(id, { + id, + type: 'interval', + createdAt: Date.now(), + }); + return id; + }; + // Hook setImmediate + global.setImmediate = function (callback, ...args) { + const id = self.original.setImmediate((...callbackArgs) => { + self.timers.delete(id); // 执行完后移除 + callback(...callbackArgs); + }, ...args); + self.timers.set(id, { + id, + type: 'immediate', + createdAt: Date.now(), + }); + return id; + }; + // Hook clear方法(确保从追踪中移除) + global.clearTimeout = ((id) => { + self.timers.delete(id); + return self.original.clearTimeout(id); + }); + global.clearInterval = ((id) => { + self.timers.delete(id); + return self.original.clearInterval(id); + }); + global.clearImmediate = ((id) => { + self.timers.delete(id); + return self.original.clearImmediate(id); + }); + this.isHooked = true; + console.log('[TimerManager] Hooked global timer functions'); + } + /** + * 恢复原始的全局定时器函数 + */ + unhook() { + if (!this.isHooked) { + return; + } + global.setTimeout = this.original.setTimeout; + global.setInterval = this.original.setInterval; + global.setImmediate = this.original.setImmediate; + global.clearTimeout = this.original.clearTimeout; + global.clearInterval = this.original.clearInterval; + global.clearImmediate = this.original.clearImmediate; + this.isHooked = false; + } + /** + * 清除所有被追踪的定时器 + */ + clearAll() { + const count = this.timers.size; + this.timers.forEach((record) => { + if (record.type === 'immediate') { + this.original.clearImmediate.call(null, record.id); + } + else { + this.original.clearTimeout.call(null, record.id); + } + }); + this.timers.clear(); + return count; + } + /** + * 按类型清除定时器 + */ + clearByType(type) { + let count = 0; + this.timers.forEach((record, id) => { + if (record.type === type) { + if (type === 'immediate') { + this.original.clearImmediate.call(null, id); + } + else { + this.original.clearTimeout.call(null, id); + } + this.timers.delete(id); + count++; + } + }); + return count; + } + /** + * 获取当前活跃的定时器数量 + */ + getActiveCount() { + return this.timers.size; + } + /** + * 获取定时器统计信息 + */ + getStats() { + const stats = { + timeout: 0, + interval: 0, + immediate: 0, + }; + this.timers.forEach((record) => { + stats[record.type]++; + }); + return stats; + } + /** + * 获取所有定时器的详细信息(用于调试) + */ + getTimers() { + return Array.from(this.timers.values()); + } +} +exports.timerManager = new GlobalTimerManager(); +const hookTimers = () => exports.timerManager.hook(); +exports.hookTimers = hookTimers; +const unhookTimers = () => exports.timerManager.unhook(); +exports.unhookTimers = unhookTimers; +const clearAllTimers = () => exports.timerManager.clearAll(); +exports.clearAllTimers = clearAllTimers; +const getTimerStats = () => exports.timerManager.getStats(); +exports.getTimerStats = getTimerStats; diff --git a/lib/server/watch.js b/lib/server/watch.js index d0dae67..67209ec 100644 --- a/lib/server/watch.js +++ b/lib/server/watch.js @@ -9,6 +9,7 @@ const dayjs_1 = tslib_1.__importDefault(require("dayjs")); const fs_1 = tslib_1.__importDefault(require("fs")); const polyfill_1 = require("./polyfill"); const tsc_alias_1 = require("tsc-alias"); +const timer_manager_1 = require("./timer-manager"); // 创建事件发射器 const createEventEmitter = () => { const listeners = new Map(); @@ -228,6 +229,7 @@ const createServerManager = (projectPath, eventEmitter, config) => { const simpleConnector = require(path_1.default.join(projectPath, "lib/config/connector")).default; console.warn("----> Starting service......"); try { + (0, timer_manager_1.clearAllTimers)(); // 这里注意要在require之前,因为require会触发编译 const { startup } = require('./start'); shutdown = await startup(pwd, simpleConnector).then((shutdown) => { @@ -606,6 +608,8 @@ const watch = async (projectPath, config) => { // 初始化polyfill polyfillLoader(); realConfig.polyfill.console.enable && (0, polyfill_1.polyfillConsole)("watch", enableTrace, realConfig.polyfill?.console?.formatter); + // 监听定时器相关的API,确保定时器在重启后不会重复触发 + (0, timer_manager_1.hookTimers)(); // 初始编译检查 const initialCompile = () => { const serverConfigFile = path_1.default.join(projectPath, "lib/configuration/server.js"); diff --git a/src/server/timer-manager.ts b/src/server/timer-manager.ts new file mode 100644 index 0000000..e49b24f --- /dev/null +++ b/src/server/timer-manager.ts @@ -0,0 +1,211 @@ +// timer-manager.ts + +type TimerType = 'timeout' | 'interval' | 'immediate'; + +interface TimerRecord { + id: NodeJS.Timeout | NodeJS.Immediate; + type: TimerType; + createdAt: number; +} + +class GlobalTimerManager { + private timers = new Map(); + private isHooked = false; + + // 保存原始函数 + private readonly original = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + setImmediate: global.setImmediate,clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval, + clearImmediate: global.clearImmediate,}; + + /** + * 开始拦截全局定时器 + */ + hook(): void { + if (this.isHooked) { + console.warn('[TimerManager] Already hooked'); + return; + } + + const self = this; + + // Hook setTimeout + global.setTimeout = function ( + callback: (...args: any[]) => void, + ms?: number, + ...args: any[] + ): any { + const id = self.original.setTimeout( + (...callbackArgs: any[]) => { + self.timers.delete(id); // 执行完后移除 + callback(...callbackArgs); + }, + ms, + ...args + ); + + self.timers.set(id, { + id, + type: 'timeout', + createdAt: Date.now(), + }); + + return id; + } as any; + + // Hook setInterval + global.setInterval = function ( + callback: (...args: any[]) => void, + ms?: number, + ...args: any[] + ): any { + const id = self.original.setInterval(callback, ms, ...args); + + self.timers.set(id, { + id, + type: 'interval', + createdAt: Date.now(), + }); + + return id; + } as any; + + // Hook setImmediate + global.setImmediate = function ( + callback: (...args: any[]) => void, + ...args: any[] + ): any { + const id = self.original.setImmediate( + (...callbackArgs: any[]) => { + self.timers.delete(id); // 执行完后移除 + callback(...callbackArgs); + }, + ...args + ); + + self.timers.set(id, { + id, + type: 'immediate', + createdAt: Date.now(), + }); + + return id; + } as any; + + // Hook clear方法(确保从追踪中移除) + global.clearTimeout = ((id: any) => { + self.timers.delete(id); + return self.original.clearTimeout(id); + }) as any; + + global.clearInterval = ((id: any) => { + self.timers.delete(id); + return self.original.clearInterval(id); + }) as any; + + global.clearImmediate = ((id: any) => { + self.timers.delete(id); + return self.original.clearImmediate(id); + }) as any; + + this.isHooked = true; + console.log('[TimerManager] Hooked global timer functions'); + } + + /** + * 恢复原始的全局定时器函数 + */ + unhook(): void { + if (!this.isHooked) { + return; + } + + global.setTimeout = this.original.setTimeout; + global.setInterval = this.original.setInterval; + global.setImmediate = this.original.setImmediate; + global.clearTimeout = this.original.clearTimeout; + global.clearInterval = this.original.clearInterval; + global.clearImmediate = this.original.clearImmediate; + + this.isHooked = false; + } + + /** + * 清除所有被追踪的定时器 + */ + clearAll(): number { + const count = this.timers.size; + + this.timers.forEach((record) => { + if (record.type === 'immediate') { + this.original.clearImmediate.call(null, record.id as NodeJS.Immediate); + } else { + this.original.clearTimeout.call(null, record.id as NodeJS.Timeout); + } + }); + + this.timers.clear(); + return count; + } + + /** + * 按类型清除定时器 + */ + clearByType(type: TimerType): number { + let count = 0; + + this.timers.forEach((record, id) => { + if (record.type === type) { + if (type === 'immediate') { + this.original.clearImmediate.call(null, id as NodeJS.Immediate); + } else { + this.original.clearTimeout.call(null, id as NodeJS.Timeout); + } + this.timers.delete(id); + count++; + } + }); + + return count; + } + + /** + * 获取当前活跃的定时器数量 + */ + getActiveCount(): number { + return this.timers.size; + } + + /** + * 获取定时器统计信息 + */ + getStats(): Record { + const stats: Record = { + timeout: 0, + interval: 0, + immediate: 0, + }; + + this.timers.forEach((record) => { + stats[record.type]++; + }); + + return stats; + } + + /** + * 获取所有定时器的详细信息(用于调试) + */ + getTimers(): TimerRecord[] { + return Array.from(this.timers.values()); + } +} + +export const timerManager = new GlobalTimerManager(); + +export const hookTimers = () => timerManager.hook(); +export const unhookTimers = () => timerManager.unhook(); +export const clearAllTimers = () => timerManager.clearAll(); +export const getTimerStats = () => timerManager.getStats(); \ No newline at end of file diff --git a/src/server/watch.ts b/src/server/watch.ts index dfed001..de75ddc 100644 --- a/src/server/watch.ts +++ b/src/server/watch.ts @@ -7,6 +7,7 @@ import fs from "fs"; import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; import { LogFormatter, LogFormatterProp, polyfillConsole } from "./polyfill"; import { prepareSingleFileReplaceTscAliasPaths, SingleFileReplacer } from 'tsc-alias'; +import { clearAllTimers, hookTimers } from "./timer-manager"; /* * 工作流程 @@ -399,6 +400,7 @@ const createServerManager = ( console.warn("----> Starting service......"); try { + clearAllTimers(); // 这里注意要在require之前,因为require会触发编译 const { startup } = require('./start') as { startup: (pwd: string, connector: any) => Promise<() => Promise>; @@ -879,6 +881,9 @@ export const watch = async ( polyfillLoader(); realConfig.polyfill.console.enable && polyfillConsole("watch", enableTrace, realConfig.polyfill?.console?.formatter); + // 监听定时器相关的API,确保定时器在重启后不会重复触发 + hookTimers() + // 初始编译检查 const initialCompile = () => { const serverConfigFile = pathLib.join(projectPath, "lib/configuration/server.js");