功能描述
Node.js 性能平台的诊断功能,大都从某一个特定角度,例如针对内存问题的堆快照,针对CPU问题的Profiling,通过一定时间的信息采集来协助定位问题。
诊断报告则从一个全局的视角抓住进程的瞬时状态,采集了堆栈,系统资源,平台信息等以协助故障定位。尤其是其中的JavaScriptStack
可以协助精确定位长正则和死循环故障。
各个分支开始支持的版本:
2.x
:v2.5.2
3.x
:v3.11.8
4.x
:v4.3.0
注意:此功能需要您的 agenthub/egg-alinode 依赖的 commandx 版本 >= v1.5.3,否则会失败。
操作指南
在实例页抓取性能数据
,或者数据趋势页,点击诊断报告
后,以json
格式输出当前应用运行时状态,协助定位故障。
下面列举一些对定位问题协助较大的输出项,具体可以实际操作生成诊断报告后查看。
JavaScriptStack
:JS 栈,当前js代码运行栈NativeStack
:当前c++运行栈JsHeapGcInfo
:堆上各个空间信息resource
:进程资源信息和eventloop资源信息libuvHandleSummary
:libuvHandle信息system
:系统环境变量,资源限制,动态链接库等FileName
:诊断报告全路径NodeJsVersion
:对应的Node.js版本alinodeVersion
:Node.js 性能平台运行时版本execPath
:运行时二进制文件路径
在线分析
文件 页面等待诊断报告生成后,点击 转储
按钮后,可以将您的应用生成的对应诊断报告文件转储至云端,此时点击 分析
按钮可以实现在线的分析:
新打开的在线分析页面会给出当前诊断报告对应的 JavaScript 栈、Native 栈、堆内分布、Libuv 句柄 和 系统详情 等信息,具体我们看一看下面的应用示例。
应用示例
复制 demo.js 代码到服务器/本地保存为demo.js后运行(这里假设
agenthub
已经运行)。
$node demos.js
http listen at 8080 pid: 26542
在浏览器打开 http://[您的服务器地址]:8080 。
选择测试
Trigger Long Running Regular Expression
或者Trigger Dead Loop JS function
进入长正则或者死循环中。然后根据上面的pid去Node.js性能平台上点击
诊断报告
,然后到文件
页面转储后继续点击分析
进行在线分析定位。
首先可以看到最有用的 JavaScript 栈 信息:
这里进程显然是阻塞在 regexpCase()
函数调用中,查看对应的 18 行 23 列可以看到是里面的 replace
操作由于触发了异常的正则回溯导致的进程全局阻塞。
剩余的一些信息的展示可以帮助您更精准地定位到问题,如下图所示
Native 栈信息
堆内分布
Libuv 句柄信息
系统详情信息(包含了进程当前的 ENV、系统软硬资源限制和共享库等信息)
其他说明
诊断报告参考了
node-report
的功能可以通过环境变量
DIAGNOSTIC_EVENTS
=fatalerror
、exception
或者fatalerror+exception
来使能遇到FatalError
或者Exception
时自动生成诊断报告。
demo.js 代码
'use strict';
const http = require('http');
const regexpCase = function() {
let str = '<br/> ' +
' 早餐后自由活动,于指定时间集合自行办理退房手续。';
str += '<br/> <br/>' +
' <br/> ' +
' <br/>';
str += ' <br/>' +
' ' +
' ' +
' <br/>';
str += ' <br/> <br/>';
str += ' ' +
' ' +
' 根据船班时间,自行前往暹粒机场,返回中国。<br/>';
str += '如需送机服务,需增加280/每单。<br/>';
let r = String(str).replace(/(^(\s*?<br[\s\/]*?>\*?)+|(\s*?<br[\s\/]*?>\s*?)+?$)/igm, '');
};
const findSomething = function(from) {
// find something from 'from'
return [];
};
const deadLoopCase = function() {
let selected = [];
let curArr = ['item1', 'item2', 'item3'];
let arr = [];
while(selected.length < curArr.length) {
const dp = findSomething(curArr)[0];
//if (typeof dp === 'undefined') {
// break;
//}
if (selected.indexOf(dp) === -1) {
selected.push(dp);
} else {
arr = arr.filter(val => {
return val != dp
})
}
}
};
http.createServer( (req, res) => {
console.log(req.url);
switch(req.url) {
case '/regexp': {
regexpCase();
res.end('regexp case');
} break;
case '/deadloop': {
deadLoopCase();
res.end('deadloop case');
} break;
case '/exit': {
process.exit(0);
} break;
default: {
res.writeHead(200, "OK",{'Content-Type': 'text/html'});
res.write('<html><head><title>Node.js</title></head><body style="font-family:arial;">');
res.write('<h2> Regular Expression and Dead Loop Detection Example</h2>');
res.write('<p>Click on button below to trigger long time running regexp test.');
res.write('<form enctype="application/x-www-form-urlencoded" action="/regexp" method="post">');
res.write('<button>Trigger Long Running Regular Expression</button></form>');
res.write('<p>Click on button below to enter JavaScript dead loop test');
res.write('<form enctype="application/x-www-form-urlencoded" action="/deadloop" method="post">');
res.write('<button>Trigger Dead Loop JS function</button></form>');
res.write('<p>The test can be terminated only before the above cases triggered.');
res.write('<form enctype="application/x-www-form-urlencoded" action="/exit" method="post">');
res.write('<button>Exit Test</button></form>');
res.write('</form></body></html');
res.end();
} break;
}
}).listen(8080);
console.log('\nhttp listen at 8080 pid: ', process.pid);