网关心跳每 5 秒发一次,监控上连续三次间隔 8 到 13 秒。同一时间 /health 接口 P99 从 20ms 涨到 6.8s,单核 CPU 跑满 100%。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
const { monitorEventLoopDelay } = require('perf_hooks');
const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
setInterval(() => console.log(h.mean / 1e6, h.max / 1e6), 5000);
同步循环在单线程里跑,定时器的回调排在这段代码后面,到期了也只能等它跑完。事件循环每一轮都要把 poll 阶段的回调清空,长任务把整轮拉长,心跳就跟着漂。这一点先确认清楚,后面就不用反复试。
「事件循环阻塞」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。
改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。
具体做法是,用 perf_hooks 的 monitorEventLoopDelay 拿到延迟分布;找到占主线程的那个函数 改成流式或分片处理;大循环每处理 N 条让出一次事件循环;CPU 密集的活挪到 worker_threads。
改完照着核一遍,monitorEventLoopDelay 的 max 从 8 秒回落到 30ms 以内 心跳间隔稳定在 5 秒。
