

























@@ -4,6 +4,7 @@ const EVENT_LOOP_MONITOR_RESOLUTION_MS = 20;
44const EVENT_LOOP_DELAY_WARN_MS = 1_000;
55const EVENT_LOOP_UTILIZATION_WARN = 0.95;
66const CPU_CORE_RATIO_WARN = 0.9;
7+const SUSTAINED_LOAD_SAMPLE_MIN_INTERVAL_MS = 1_000;
7889type EventLoopDelayMonitor = ReturnType<typeof monitorEventLoopDelay>;
910type EventLoopUtilization = ReturnType<typeof performance.eventLoopUtilization>;
@@ -26,6 +27,17 @@ export type GatewayEventLoopHealthMonitor = {
2627stop: () => void;
2728};
282930+type EventLoopUtilizationReader = typeof performance.eventLoopUtilization;
31+32+type EventLoopDelayMonitorFactory = (resolutionMs: number) => EventLoopDelayMonitor;
33+34+type GatewayEventLoopHealthMonitorDeps = {
35+now?: () => number;
36+cpuUsage?: typeof process.cpuUsage;
37+eventLoopUtilization?: EventLoopUtilizationReader;
38+createDelayMonitor?: EventLoopDelayMonitorFactory;
39+};
40+2941function roundMetric(value: number, digits = 3): number {
3042if (!Number.isFinite(value)) {
3143return 0;
@@ -38,14 +50,49 @@ function nanosecondsToMilliseconds(value: number): number {
3850return roundMetric(value / 1_000_000, 1);
3951}
405241-export function createGatewayEventLoopHealthMonitor(): GatewayEventLoopHealthMonitor {
53+function resolveGatewayEventLoopHealthReasons(params: {
54+intervalMs: number;
55+delayP99Ms: number;
56+delayMaxMs: number;
57+utilization: number;
58+cpuCoreRatio: number;
59+}): GatewayEventLoopHealthReason[] {
60+const reasons: GatewayEventLoopHealthReason[] = [];
61+const hasSustainedLoadWindow = params.intervalMs >= SUSTAINED_LOAD_SAMPLE_MIN_INTERVAL_MS;
62+63+if (
64+params.delayP99Ms >= EVENT_LOOP_DELAY_WARN_MS ||
65+params.delayMaxMs >= EVENT_LOOP_DELAY_WARN_MS
66+) {
67+reasons.push("event_loop_delay");
68+}
69+if (hasSustainedLoadWindow && params.utilization >= EVENT_LOOP_UTILIZATION_WARN) {
70+reasons.push("event_loop_utilization");
71+}
72+if (hasSustainedLoadWindow && params.cpuCoreRatio >= CPU_CORE_RATIO_WARN) {
73+reasons.push("cpu");
74+}
75+76+return reasons;
77+}
78+79+export function createGatewayEventLoopHealthMonitor(
80+deps: GatewayEventLoopHealthMonitorDeps = {},
81+): GatewayEventLoopHealthMonitor {
82+const nowMs = deps.now ?? Date.now;
83+const readCpuUsage = deps.cpuUsage ?? process.cpuUsage.bind(process);
84+const readEventLoopUtilization =
85+deps.eventLoopUtilization ?? performance.eventLoopUtilization.bind(performance);
86+const createDelayMonitor =
87+deps.createDelayMonitor ??
88+((resolutionMs: number) => monitorEventLoopDelay({ resolution: resolutionMs }));
4289let monitor: EventLoopDelayMonitor | null = null;
43-let lastWallAt = Date.now();
44-let lastCpuUsage: CpuUsage | null = process.cpuUsage();
45-let lastEventLoopUtilization: EventLoopUtilization | null = performance.eventLoopUtilization();
90+let lastWallAt = nowMs();
91+let lastCpuUsage: CpuUsage | null = readCpuUsage();
92+let lastEventLoopUtilization: EventLoopUtilization | null = readEventLoopUtilization();
46934794try {
48-monitor = monitorEventLoopDelay({ resolution: EVENT_LOOP_MONITOR_RESOLUTION_MS });
95+monitor = createDelayMonitor(EVENT_LOOP_MONITOR_RESOLUTION_MS);
4996monitor.enable();
5097monitor.reset();
5198} catch {
@@ -58,33 +105,36 @@ export function createGatewayEventLoopHealthMonitor(): GatewayEventLoopHealthMon
58105return undefined;
59106}
6010761-const now = Date.now();
108+const now = nowMs();
62109const intervalMs = Math.max(1, now - lastWallAt);
63-const cpuUsage = process.cpuUsage(lastCpuUsage);
64-const currentEventLoopUtilization = performance.eventLoopUtilization();
65-const utilization = roundMetric(
66-performance.eventLoopUtilization(currentEventLoopUtilization, lastEventLoopUtilization)
67-.utilization,
68-);
69110const delayP99Ms = nanosecondsToMilliseconds(monitor.percentile(99));
70111const delayMaxMs = nanosecondsToMilliseconds(monitor.max);
71-const cpuTotalMs = roundMetric((cpuUsage.user + cpuUsage.system) / 1_000, 1);
72-const cpuCoreRatio = roundMetric(cpuTotalMs / intervalMs);
73-const reasons: GatewayEventLoopHealthReason[] = [];
112+const hasDelayWarning =
113+delayP99Ms >= EVENT_LOOP_DELAY_WARN_MS || delayMaxMs >= EVENT_LOOP_DELAY_WARN_MS;
7411475-if (delayP99Ms >= EVENT_LOOP_DELAY_WARN_MS || delayMaxMs >= EVENT_LOOP_DELAY_WARN_MS) {
76-reasons.push("event_loop_delay");
77-}
78-if (utilization >= EVENT_LOOP_UTILIZATION_WARN) {
79-reasons.push("event_loop_utilization");
80-}
81-if (cpuCoreRatio >= CPU_CORE_RATIO_WARN) {
82-reasons.push("cpu");
115+if (!hasDelayWarning && intervalMs < SUSTAINED_LOAD_SAMPLE_MIN_INTERVAL_MS) {
116+monitor.reset();
117+return undefined;
83118}
84119120+const cpuUsage = readCpuUsage(lastCpuUsage);
121+const currentEventLoopUtilization = readEventLoopUtilization();
122+const utilization = roundMetric(
123+readEventLoopUtilization(currentEventLoopUtilization, lastEventLoopUtilization).utilization,
124+);
125+const cpuTotalMs = roundMetric((cpuUsage.user + cpuUsage.system) / 1_000, 1);
126+const cpuCoreRatio = roundMetric(cpuTotalMs / intervalMs);
127+const reasons = resolveGatewayEventLoopHealthReasons({
128+ intervalMs,
129+ delayP99Ms,
130+ delayMaxMs,
131+ utilization,
132+ cpuCoreRatio,
133+});
134+85135monitor.reset();
86136lastWallAt = now;
87-lastCpuUsage = process.cpuUsage();
137+lastCpuUsage = readCpuUsage();
88138lastEventLoopUtilization = currentEventLoopUtilization;
8913990140return {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。