我有一个node.js服务器在生产中冻结,它似乎是由JSONStream内部的无限循环引起的。下面是从冻结服务器的核心转储中捕获的堆栈跟踪:
1: toString [buffer.js:~392] (this=0x1e28fb6d25c9 <a Buffer>#1#,encoding=0x266ee104121 <undefined>,start=0x266ee104121 <undefined>,end=0x266ee104121 <undefined>)
2: arguments adaptor frame: 0->3
3: write [/home/deploy/node_modules/JSONStream/node_modules/jsonparse/jsonparse.js:136] (this=0x32cc8dd5a999 <a Parser>#2#,buffer=0x32cc8dd5aa49 <a Buffer>#3#)
4: /* anonymous */ [/home/deploy/node_modules/JSONStream/index.js:~17] (this=0x32cc8dd5ab11 <a Stream>#4#,chunk=0x32cc8dd5aa49 <a Buffer>#3#)
5: write [/home/deploy/node_modules/JSONStream/node_modules/through/index.js:~24] (this=0x32cc8dd5ab11 <a Stream>#4#,data=0x32cc8dd5aa49 <a Buffer>#3#)
6: write [_stream_readable.js:~582] (this=0x266ee106c91 <JS Global Object>#5#,dest=0x32cc8dd5ab11 <a Stream>#4#,i=0,list=0x266ee104101 <null>)
7: flow [_stream_readable.js:592] (this=0x266ee106c91 <JS Global Object>#5#,src=0x32cc8dd5ac69 <an IncomingMessage>#6#)
8: /* anonymous */ [_stream_readable.js:560] (this=0x266ee106c91 <JS Global Object>#5#)
9: _tickCallback [node.js:415] (this=0x29e7331bb2a1 <a process>#7#)
我如何找到这个无限循环的来源?
不幸的是,服务器正在生产中运行,并且正在处理成千上万的请求,因此很难给出任何额外的上下文。服务器的基本功能是为其他服务发出出站HTTP请求。
值得注意的是,我不认为这是由内存泄漏引起的。在这些冻结事件期间,服务器的内存使用保持不变(并且很低),而CPU的使用峰值达到99%
得出无限循环结论的另一个证据是事件循环本身似乎已经停止。当我在setInterval中放入console.log时,服务器将在冻结后立即停止输出。
我们已经验证了这个问题不是由过期/损坏的套接字连接引起的,通过将最大连接数设置为无穷大(这禁用了它们在node.js中的重用)
我们正在使用JSONStream 0.7.1(其中包含默认的jsonparse 0.0.5版本)。我们在JSONStream repo上发现了这个问题,并尝试了JSONParse的分支,只更新到最新的JSONParse版本。
您的问题似乎是由这一行jsonstream@0.0.5
引起的。虽然如果没有转储服务器上的内容,我无法最终为您解决这个问题,但这似乎表明您的缓冲区非常大。
这也解释了为什么你的服务器正在锁定(正如你在聊天中提到的),为什么事件循环不进行,为什么你的内存没有上升到天空,但你的CPU;这里可能发生的情况是,你试图toString()
一个难以置信的大字节,你的硬件根本无法做到,它死机了。
请务必将这条线索的进一步调查报告给我。我觉得这是不可避免的,你将不得不在一个开发盒上重现这个问题。这可能很简单,只需在缓冲区中添加一些完整性检查,并确保它不会超过一定的大小。
上面的代码部分只在n <= 128
时被击中,所以如果你使用有符号字符(如Unicode)或有符号字节,那么你可能会在正常函数中遇到这个问题…也许可以考虑一下!