我有一个使用 express 和 redis 的节点应用程序。在我们的开发服务器上,经过一段时间的使用 node 开始使用 100% cpu。应用程序仍然响应,但顶部报告节点使用 100%。在重新启动节点之前,cpu 不会下降。
我没有将其确定为导致它的任何特定路线或功能。
诊断此问题的最佳方法是什么?
我用 v8-profiler 查看了节点检查器,它给了我这里报告的相同错误 https://github.com/dannycoates/v8-profiler/issues/10
我有一个使用 express 和 redis 的节点应用程序。在我们的开发服务器上,经过一段时间的使用 node 开始使用 100% cpu。应用程序仍然响应,但顶部报告节点使用 100%。在重新启动节点之前,cpu 不会下降。
我没有将其确定为导致它的任何特定路线或功能。
诊断此问题的最佳方法是什么?
我用 v8-profiler 查看了节点检查器,它给了我这里报告的相同错误 https://github.com/dannycoates/v8-profiler/issues/10
您可以使用node-tick分析您的应用程序。
node-tick
方式sudo npm -g install tick
node --prof ./app.js
node-tick-processor
并解释结果我通过编写脚本记录每个请求然后重放它们来发现问题。
问题是因为我有一个没有被返回的回调。
myAsncFunc(function(err, data) {
if (err) { callback(err) }
//node kept going after the error was returned to the user.
// make sure you, return callback(err)
})
这是我的 replay.js 代码,供任何感兴趣的人使用。
var request = require('request');
var async = require('async');
var redis = require('redis');
var host = 'http://myhost.com';
var jobs = true;
var client = redis.createClient();
async.whilst(
function () { return jobs; },
function (callback) {
client.lpop('history', function(err, url) {
console.log(url);
if (!url) {
jobs = false;
callback();
}
request.get({url:host+url}, function() {
callback();
});
})
},
function (err) {
console.log('done')
}
);
而在你是快递应用程序。
app.get('/*', function(req, res, next) {
var url = req.originalUrl;
redis.rpush('history', url);
next();
});
这很酷,因为播放的每个历史项目都会再次添加到队列中,因此它会不断循环,并且每次您访问新页面时,都会将其添加到队列中。
持续以 100% CPU 运行是无限循环的典型特征。这是单线程 nodejs 中的一个真正问题,但不幸的是,它缺乏相关信息。尽管您声明您的服务器仍然响应并且无限循环不是您的情况,但您仍然可以找到有用的提示来调试实时 nodejs 应用程序。
最终我找到了唯一有用的文章:如何在 nodejs 中跟踪死循环:
通过 SSH 连接到您的服务器。识别 nodejs 进程 ID。现在,让我们告诉进程监听调试请求。是的,我们正在使用一个名为 kill 的命令。不,我们不会终止进程。我们正在向它发送不同的信号。
kill -SIGUSR1 4702
执行此操作后,该过程将打开调试器连接。实际上,它会在其控制台日志中打印一个特殊的 URL,您可以在 Chrome 中打开该 URL 来调试进程!但是,也许您不想为了建立连接而在防火墙和容器配置中钻一个洞。是的,我也没有。因此,让我们在命令行中进行调试:
node inspect -p 4702
你会看到这个提示:
debug>
然后输入:
pause
你回来了:
break in file:///somewhere/something.js:555
>555 for (prop in inputObject) {
510 if (hasOwnProp(inputObject, prop)) {
511 normalizedProp = normalizeUnits(prop);
是的!我们有第一个提示。该应用程序正在执行文件 something.js 中的第 555 行。这可能足以立即看到错误。但通常我们需要比这更多的信息。您可以键入 backtrace 以获取完整的堆栈跟踪:
#0 someFunctionName file:///somewhere/somefile.js:444:22
#1 someFunctionName file:///somewhere/somefile.js:555:33
#2 someFunctionName file:///somewhere/somefile.js:666:44
… 等等。
如果您使用 webpack 的 UI 应用程序,请注意watchOptions
或watch
。对我来说,禁用民意调查可以解决问题
watchOptions: {
poll: false
}
或者您可以设置一个时间,当轮询将被触发时poll: 3000
(每 3 秒一次)
https://webpack.js.org/configuration/watch/#watchoptionsignored
在我关闭主管模式之前,我也经历了 100% 的 CPU 使用率(当文件更改时导致节点重新启动)。
这可能无法回答这个问题,但如果像我这样的新手担心 CPU 使用率,可能就是这种情况。
如果您nodemon
用来监视文件,请考虑使用文件较少的文件夹的路径。例如,让nodemon
使用 bower 或 npm 安装的监视库文件夹会导致 CPU 使用率很高,因为其中包含数千个文件。
这是我的示例nodemon.json
文件:
{
"watch": ["views","routes"],
"ext": "html, js"
}
奇迹般有效。
也许你在某处有一些计算nextTick
正在不断地浪费 CPU。
如果您无法运行配置文件,那么很难找出哪种方法正在破坏 cpu。另一件事是使用记录器中间件http://senchalabs.github.com/connect/middleware-logger.html检查快速日志
另一种选择是我们可以使用火焰图并查看函数调用导致 cpu 高。命令可以找到如下
$> git clone https://github.com/brendangregg/FlameGraph.git
$> perf record -F 99 -p 1812 /*process id*/ -g --call-graph dwarf
$> perf script > out.perf
$> FlameGraph/stackcollapse-perf.pl out.perf > out.folded
$> FlameGraph/flamegraph.pl out.folded > out.svg
这可能是因为您直接拥有大量文件。例如 node_modules 文件夹。您需要使用 -i 参数来忽略该文件夹。所以它应该是这样的:
supervisor -i ./node_modules app
。