Javascript Node.js 100%CPU-gettimeofday调用
我有一个长时间运行的node.js进程,有时会跳到100%的CPU并停止响应请求。最近一次它这样做时,我将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) =
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;iexplicitdelete
,从较大的散列或数组中删除,如清理内部数据容器,也可能导致类似的症状。固有的节点操作可能非常缓慢:
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
...