如何检测和测量node.js中的事件循环阻塞?

问题描述 投票:0回答:5

我想监控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 所做的那样。

node.js npm blocking event-loop
5个回答
10
投票

“有更好的方法来获取这些信息吗?” 我没有比检查 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

“主要用途是测量间隔之间的表现。”


2
投票

代码

此代码将测量事件循环触发所需的时间(以纳秒为单位)。它测量当前进程和下一个刻度之间的时间。

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
});

编辑:添加解释,

process.hrtime([时间])

以 [秒,纳秒] 元组数组形式返回当前高分辨率实时。 time 是一个可选参数,它必须是先前 process.hrtime() 调用的结果(因此,包含先前时间的 [秒,纳秒] 元组数组中的实时时间)以与当前时间进行比较。这些时间与过去的任意时间相关,与一天中的时间无关,因此不受时钟漂移的影响。主要用途是测量间隔之间的表现。

process.nextTick(callback[, arg][, ...])

当前事件循环运行完成后,调用回调函数。

这不是 setTimeout(fn, 0) 的简单别名,它效率更高。它在事件循环的后续滴答中触发任何其他 I/O 事件(包括计时器)之前运行。


1
投票

查看这个插件https://github.com/tj/node-blocked我现在正在使用它,它似乎可以实现你想要的功能。

let blocked = require("blocked");

blocked(ms => {
  console.log("EVENT LOOP Blocked", ms);
});

将打印出事件循环被阻塞的时间(以毫秒为单位)


0
投票

您可能还想查看

node
io.js
中内置的分析。 例如,请参阅这篇文章http://www.brendangregg.com/flamegraphs.html

还有这个相关的答案如何调试 Node.js 应用程序


0
投票

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
...

注意

  • 你需要多个CPU核心,否则这个解决方案可能不会比setTimeout/setInterval解决方案更好
  • blocked
    图书馆正在使用
    setInterval
    方法
© www.soinside.com 2019 - 2024. All rights reserved.