我想监控node.js 中事件循环每次运行需要多长时间。但是我不确定衡量这一点的最佳方法。我能想到的最好的方法是这样的:
var interval = 500;
var interval = setInterval(function() {
var last = Date.now();
setImmediate(function() {
var delta = Date.now() - last;
if (delta > blockDelta) {
report("node.eventloop_blocked", delta);
}
});
}, interval);
我基本上通过查看
setInterval
的延迟来推断事件循环运行时间。我在 blocked 节点模块中看到了相同的方法,但感觉不准确且沉重。有没有更好的方法来获取这些信息?
更新:将代码更改为使用
setImmediate
,如 hapi.js 所做的那样。
“有更好的方法来获取这些信息吗?” 我没有比检查 SetImmediate 的时间延迟更好的方法来测试事件循环,但是您可以使用节点的高分辨率计时器而不是
Date.now()
获得更好的精度
var interval = 500;
var interval = setInterval(function() {
var last = process.hrtime(); // replace Date.now()
setImmediate(function() {
var delta = process.hrtime(last); // with process.hrtime()
if (delta > blockDelta) {
report("node.eventloop_blocked", delta);
}
});
}, interval);
注意:delta 将是一个元组数组 [秒,纳秒]。
有关 process.hrtime() 的更多详细信息: https://nodejs.org/api/all.html#all_process_hrtime
“主要用途是测量间隔之间的表现。”
此代码将测量事件循环触发所需的时间(以纳秒为单位)。它测量当前进程和下一个刻度之间的时间。
var time = process.hrtime();
process.nextTick(function() {
var diff = process.hrtime(time);
console.log('benchmark took %d nanoseconds', diff[0] * 1e9 + diff[1]);
// benchmark took 1000000527 nanoseconds
});
编辑:添加解释,
以 [秒,纳秒] 元组数组形式返回当前高分辨率实时。 time 是一个可选参数,它必须是先前 process.hrtime() 调用的结果(因此,包含先前时间的 [秒,纳秒] 元组数组中的实时时间)以与当前时间进行比较。这些时间与过去的任意时间相关,与一天中的时间无关,因此不受时钟漂移的影响。主要用途是测量间隔之间的表现。
当前事件循环运行完成后,调用回调函数。
这不是 setTimeout(fn, 0) 的简单别名,它效率更高。它在事件循环的后续滴答中触发任何其他 I/O 事件(包括计时器)之前运行。
查看这个插件https://github.com/tj/node-blocked我现在正在使用它,它似乎可以实现你想要的功能。
let blocked = require("blocked");
blocked(ms => {
console.log("EVENT LOOP Blocked", ms);
});
将打印出事件循环被阻塞的时间(以毫秒为单位)
您可能还想查看
node
和 io.js
中内置的分析。 例如,请参阅这篇文章http://www.brendangregg.com/flamegraphs.html
还有这个相关的答案如何调试 Node.js 应用程序
setInterval
方法有一个重要的缺点:直到主事件循环从繁重的操作中解除阻塞后,回调函数才会被调用。
您可以通过使用集群来避免这个问题
const cluster = require("node:cluster");
const process = require("node:process");
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
cluster.fork();
cluster.on("message", (worker, message) => {
if (message.cmd === "ping") {
console.log("ping");
worker.send({ cmd: "pong" });
}
});
function blockingOperation() {
console.log("inside blocking operation");
for (let i = 0; i < 100000; i++) {
for (let j = 0; j < 100000; j++) {
i + j;
}
}
console.log("blocking operation finished");
}
setTimeout(() => {
blockingOperation();
}, 1000);
} else {
function poll() {
return new Promise((resolve) => {
cluster.worker.once("message", (message) => {
if (message.cmd === "pong") {
console.log("pong");
resolve();
}
});
process.send({ cmd: "ping" });
});
}
function timeout(interval) {
return new Promise((resolve) => {
setTimeout(() => resolve(-1), interval);
});
}
setInterval(async () => {
const result = await Promise.race([poll(), timeout(100)])
if (result === -1) {
console.log('blocked')
}
}, 500);
}
这是控制台输出
Primary 76352 is running
ping
pong
inside blocking operation
blocked
...
blocked
blocking operation finished
ping // ping-pong has blocked
ping
ping
ping
pong
pong
pong
pong // ping-pong has unblocked
ping
pong
...
注意
blocked
图书馆正在使用setInterval
方法