功能描述
Node.js 性能平臺的診斷功能,大都從某一個特定角度,例如針對內存問題的堆快照,針對CPU問題的Profiling,通過一定時間的信息采集來協(xié)助定位問題。
診斷報告則從一個全局的視角抓住進程的瞬時狀態(tài),采集了堆棧,系統(tǒng)資源,平臺信息等以協(xié)助故障定位。尤其是其中的JavaScriptStack
可以協(xié)助精確定位長正則和死循環(huán)故障。
各個分支開始支持的版本:
2.x
:v2.5.2
3.x
:v3.11.8
4.x
:v4.3.0
注意:此功能需要您的 agenthub/egg-alinode 依賴的 commandx 版本 >= v1.5.3,否則會失敗。
操作指南
在實例頁抓取性能數據
,或者數據趨勢頁,點擊診斷報告
后,以json
格式輸出當前應用運行時狀態(tài),協(xié)助定位故障。
下面列舉一些對定位問題協(xié)助較大的輸出項,具體可以實際操作生成診斷報告后查看。
JavaScriptStack
:JS 棧,當前js代碼運行棧NativeStack
:當前c++運行棧JsHeapGcInfo
:堆上各個空間信息resource
:進程資源信息和eventloop資源信息libuvHandleSummary
:libuvHandle信息system
:系統(tǒng)環(huán)境變量,資源限制,動態(tài)鏈接庫等FileName
:診斷報告全路徑NodeJsVersion
:對應的Node.js版本alinodeVersion
:Node.js 性能平臺運行時版本execPath
:運行時二進制文件路徑
在線分析
文件 頁面等待診斷報告生成后,點擊 轉儲
按鈕后,可以將您的應用生成的對應診斷報告文件轉儲至云端,此時點擊 分析
按鈕可以實現在線的分析:
新打開的在線分析頁面會給出當前診斷報告對應的 JavaScript 棧、Native 棧、堆內分布、Libuv 句柄 和 系統(tǒng)詳情 等信息,具體我們看一看下面的應用示例。
應用示例
復制 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
進入長正則或者死循環(huán)中。然后根據上面的pid去Node.js性能平臺上點擊
診斷報告
,然后到文件
頁面轉儲后繼續(xù)點擊分析
進行在線分析定位。
首先可以看到最有用的 JavaScript 棧 信息:
這里進程顯然是阻塞在 regexpCase()
函數調用中,查看對應的 18 行 23 列可以看到是里面的 replace
操作由于觸發(fā)了異常的正則回溯導致的進程全局阻塞。
剩余的一些信息的展示可以幫助您更精準地定位到問題,如下圖所示
Native 棧信息
堆內分布
Libuv 句柄信息
系統(tǒng)詳情信息(包含了進程當前的 ENV、系統(tǒng)軟硬資源限制和共享庫等信息)
其他說明
診斷報告參考了
node-report
的功能可以通過環(huán)境變量
DIAGNOSTIC_EVENTS
=fatalerror
、exception
或者fatalerror+exception
來使能遇到FatalError
或者Exception
時自動生成診斷報告。
demo.js 代碼
'use strict';
const http = require('http');
const regexpCase = function() {
let str = '<br/> ' +
' 早餐后自由活動,于指定時間集合自行辦理退房手續(xù)。';
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);