Javascript Node.js 100%CPU-gettimeofday调用

Javascript Node.js 100%CPU-gettimeofday调用,javascript,linux,node.js,strace,Javascript,Linux,Node.js,Strace,我有一个长时间运行的node.js进程,有时会跳到100%的CPU并停止响应请求。最近一次它这样做时,我将strace附加到流程中,下面是我看到的: Process 19523 attached - interrupt to quit gettimeofday({1394558574, 234192}, NULL) = 0 gettimeofday({1394558574, 235381}, NULL) = 0 gettimeofday({1394558574, 306460}, NULL) =

我有一个长时间运行的node.js进程,有时会跳到100%的CPU并停止响应请求。最近一次它这样做时,我将
strace
附加到流程中,下面是我看到的:

Process 19523 attached - interrupt to quit
gettimeofday({1394558574, 234192}, NULL) = 0
gettimeofday({1394558574, 235381}, NULL) = 0
gettimeofday({1394558574, 306460}, NULL) = 0
mmap(0x3edab823a000, 2097152, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3edab823a000
munmap(0x3edab823a000, 811008)          = 0
munmap(0x3edab8400000, 237568)          = 0
mmap(0x3edab8300000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3edab8300000
gettimeofday({1394558574, 316435}, NULL) = 0
gettimeofday({1394558574, 316576}, NULL) = 0
gettimeofday({1394558574, 316677}, NULL) = 0
gettimeofday({1394558574, 316775}, NULL) = 0
gettimeofday({1394558574, 316879}, NULL) = 0
gettimeofday({1394558574, 316978}, NULL) = 0
gettimeofday({1394558574, 317439}, NULL) = 0
gettimeofday({1394558574, 317537}, NULL) = 0
gettimeofday({1394558574, 318139}, NULL) = 0
gettimeofday({1394558574, 318234}, NULL) = 0
gettimeofday({1394558574, 318343}, NULL) = 0
gettimeofday({1394558574, 318437}, NULL) = 0
gettimeofday({1394558574, 318530}, NULL) = 0
gettimeofday({1394558574, 318624}, NULL) = 0
gettimeofday({1394558574, 319135}, NULL) = 0
gettimeofday({1394558574, 319648}, NULL) = 0
gettimeofday({1394558574, 319769}, NULL) = 0
gettimeofday({1394558574, 319975}, NULL) = 0
futex(0x7f5b380008c8, FUTEX_WAKE_PRIVATE, 1) = 1
gettimeofday({1394558574, 322266}, NULL) = 0
gettimeofday({1394558574, 322426}, NULL) = 0
gettimeofday({1394558574, 322520}, NULL) = 0
gettimeofday({1394558574, 322759}, NULL) = 0
gettimeofday({1394558574, 322853}, NULL) = 0
gettimeofday({1394558574, 322995}, NULL) = 0
futex(0x7f5b380008c8, FUTEX_WAKE_PRIVATE, 1) = 1
gettimeofday({1394558574, 417614}, NULL) = 0
gettimeofday({1394558575, 386566}, NULL) = 0
gettimeofday({1394558575, 387704}, NULL) = 0
gettimeofday({1394558575, 463410}, NULL) = 0
mmap(0x24cfd260f000, 2097152, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x24cfd260f000
munmap(0x24cfd260f000, 987136)          = 0
munmap(0x24cfd2800000, 61440)           = 0
mmap(0x24cfd2700000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x24cfd2700000
这是大量的
gettimeofday
调用,其他的就不多了!什么可能导致node.js像这样卡住

更新:我从旧版本的node.js升级到了10.29(我想),这就消失了。我刚升级到10.33,问题又出现了。这次我在调试上取得了一些进展。第一步:

$ sudo strace -p 11812 -c
Process 11812 attached - interrupt to quit
Process 11812 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000192           0      2780           gettimeofday
  0.00    0.000000           0      1390           getrusage
  0.00    0.000000           0        31           futex
  0.00    0.000000           0      1390           clock_gettime
------ ----------- ----------- --------- --------- ----------------
100.00    0.000192                  5591           total
从中,我了解到了如何将节点调试器附加到正在运行的进程,下面是我得到的信息:

$ sudo kill -s SIGUSR1 11812
$ sudo node debug -p 11812
connecting... ok
break in timers.js:79
  77 }
  78
  79 function listOnTimeout() {
  80   var msecs = this.msecs;
  81   var list = this;
debug> bt
#0 timers.js:79:23

因此,它看起来确实与计时器有关,但我不知道如何进一步向上移动堆栈,以找出在我的代码中触发问题的位置。

我猜是有人手动实现了阻塞“setTimeout”。如果有人不想为了防止潜在的争用情况而释放对主JS线程的控制,可能会发生这种情况

出于显而易见的原因,这对于生产代码来说是一种不好的做法,但我有时在调试中使用它来强制异步进程按特定顺序执行

你可以找一些像这样愚蠢的东西:

var mockAsyncFunction = function (cb) {
    setTimeout(function () {
        cb(null, 'dummy_result')
    }, 5000);
};

var myResult = null;
mockAsyncFunction(function (err, result) {
    myResult = result;
});
var timeOut = 10000; // timeout in 10 sec.
var timeStart = new Date().getTime();


while (1) {
    if (new Date().getTime() - timeStart > 10000) {
        break;
    }
}
console.log('DONE');
或者nextTick递归更邪恶的东西,比如:

var timeStart = new Date().getTime();

var recurseUntilDone = function () {
    if (new Date().getTime() - timeStart < 10000) {
        process.nextTick(recurseUntilDone);
    } else {
        console.log('Done recursing');
    }
};

recurseUntilDone();
var timeStart=new Date().getTime();
var recurseUntilDone=函数(){
如果(新日期().getTime()-timeStart<10000){
process.nextTick(recurseUntilDone);
}否则{
log('Done recursing');
}
};
recurseUntilDone();
用于在CPU为100%时暂停您的代码-我打赌在某个特定时间过去之前,执行的循环检查状态也很糟糕,但它们可能很难找到


在启动节点时使用--debug(即
node index.js--debug
)附加调试器,并在单独的窗口中运行
节点检查器
。使用Chrome连接到调试会话(url从node inspector命令输出),等待问题出现并暂停执行,您应该能够找到它。

我们在生产和开发中也看到了这一点。也在寻找答案。 我们开始研究节点运行时,但问题很少出现,因此没有任何优先级。因为它完全受cpu限制,没有系统调用,所以很难用strace捕获


它不会是定时循环检查状态,因为新的Date().getTime()循环不会对gettimeofday进行任何调用(在节点v0.10.29中;它只在一个pid中执行一长串nanosleeps,而在另一个pid中只执行futex调用。实际上很聪明)。Date.now()也是如此。

不知道它是否相关,但这里有一种简单的方法可以粉碎节点进程:当数组中有数据时,将其用作队列。症状是处理速度非常慢,cpu使用率为100%

//a = [];
a = new Array();

for (i=0; i<87370; i++) a.push({});

for (i=0; i<1000000; i++) {
    a.shift();
    a.push({});
    if (i%1000 === 0) {
    process.stdout.write(".");
    //global.gc();
    }
}

// node v0.10.29: 10k:  0.15 sec
//        80k:  0.17 sec
//        87369: 0.17 sec
//            87370: instant 41k, then .9k per second
//        87400: Array: instant 41k, then .9k per second
//        87400: []: instant 28k, then .9k per second
//        87400: Array, pushing 1 or 'x': .9k per second from the start
// node v0.11.13: 10k: 1.94 sec
//        16683: 3.87 sec
//        16884: uniform 237.16 sec
//a=[];
a=新数组();

对于(i=0;iexplicit
delete
,从较大的散列或数组中删除,如清理内部数据容器,也可能导致类似的症状。固有的节点操作可能非常缓慢:

h = {}
for (i=0; i<200000; i++) h[i] = i;     // 25,000,000 / sec
for (i=0; i<1000; i++) delete h[i];    // 11,000 / sec
for (i=0; i<200000; i++) delete h[i];  // 7,700 / sec

a = new Array();
for (i=0; i<200000; i++) a[i] = i;     // 50,000,000 / sec
for (i=0; i<1000; i++) delete a[i];    // 10,000 / sec
for (i=0; i<200000; i++) delete a[i];  // 8,000 / sec
// and out of curiousity...
for (i=0; i<200000; i++) a[i];         // 250,000,000 / sec
for (i=1; i<200000; i++) a[i-1] = a[i]; // 180,000,000 / sec

如果有人仍然存在此问题,我也会遇到此问题(严重到我的“repeat”节点开始每秒丢弃指令)

在我的例子中,这是由“串行”节点引起的-您可以通过启动
htop
命令来检查它,以监视CPU使用情况,然后在删除“串行输入”节点后重新部署所有节点


CPU使用率应立即下降。

您使用的是“时间条件变量”吗?@AlexejMagura这是一个大型应用程序,因此我无法将其全部发布,我不知道从何处开始追踪相关代码。我建议使用探查器来找出漏洞的来源,而不是通过更改代码手动搜索。请看:github.com/felixge/node-memory-leak-tutorial和/或github.com/node-IInspector/node Inspector您是否尝试过通过JSHint或其他分析工具运行所有代码?我怀疑这是一个永无止境的循环。从目前为止所说的话来概括一下:您是否有任何类型的队列正在使用“重试”类型的事件进行备份,这些事件最终都会得到处理说它最终会恢复正常只是让我觉得(在代码或库中)可能是每个人都在争夺/检查某种资源的可用性的瓶颈。在正常操作中可能无关紧要,但如果有什么东西备份了…在使用量等方面出现时,是否有任何线索?当我查看nextTick递归时,我在节点v0.10中发现一个调用堆栈超出错误节点v0.11支持它从节点内部调用gettimeofday()的是什么?一个新的日期。getTime()循环not@Andras我想是setTimeouts使用的,我只是尝试了一下,似乎没有。它多次调用nanosleep.9ms(没有setTimeouts那么多,但有点成比例)有趣。我刚刚确认setInterval和setTimeout都调用clock_gettime,而不是gettimeofday。node.js代码中没有太多对它的引用:是的,我也使用了grepped,LinuxSemaphore::Wait看起来是可能的,但我在尝试从那里跟踪时陷入了困境,需要检查的地方太多(我在本地克隆了git的源代码)我面临着同样的问题?@Andras如果这个问题为你解决了,你能提供线索来解决这个问题吗?
...
14:08:36.327886 munmap(0x44200000, 802816) = 0
14:08:36.328145 munmap(0xf7100000, 1183744) = 0
14:08:36.328483 clock_gettime(CLOCK_REALTIME, {1418584116, 328495018}) = 0
14:08:36.328540 clock_gettime(CLOCK_REALTIME, {1418584116, 328551680}) = 0
14:08:36.328581 clock_gettime(CLOCK_REALTIME, {1418584116, 328592628}) = 0
14:08:36.328633 futex(0x8e79a7c, FUTEX_WAKE_PRIVATE, 1) = 1
14:09:03.130336 clock_gettime(CLOCK_REALTIME, {1418584143, 130406483}) = 0
14:09:03.130479 clock_gettime(CLOCK_REALTIME, {1418584143, 130503357}) = 0
14:09:03.130560 futex(0x8e79a7c, FUTEX_WAKE_PRIVATE, 1) = 1
14:09:37.090127 clock_gettime(CLOCK_REALTIME, {1418584177, 90195108}) = 0
14:09:37.090271 clock_gettime(CLOCK_REALTIME, {1418584177, 90296760}) = 0
14:09:37.090355 futex(0x8e79a7c, FUTEX_WAKE_PRIVATE, 1) = 1
14:10:17.588552 mmap2(0x3f302000, 4231168, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3f302000
14:10:17.588694 munmap(0x3f302000, 1040384) = 0
14:10:17.588768 munmap(0x3f709000, 8192) = 0
...