思考一下node在服務上的代碼調(diào)試和性能調(diào)優(yōu)

前言

在服務器上調(diào)試node,是一件救火行為东帅,因為压固,如果是本地開發(fā),通過IDE提供的調(diào)試平臺我們可以進行一步一步的調(diào)試靠闭,在遠程服務器上調(diào)試帐我,會中斷服務進程,造成其他錯誤愧膀,推薦利用單元測試工具和做好合理的日志進程調(diào)試拦键,萬不得已,后院著火了檩淋,再使用服務器上的調(diào)試方法芬为。

Debugger

V8提供了標準的調(diào)試API,這個工具在node中是通過在語句中添加debugger的形式進行的蟀悦,同時媚朦,啟動的時候也需要帶上debug參數(shù)才可以實現(xiàn)。詳見下方

node debug xxx.js
x = 5;
setTimeout(function () {
debugger;
console.log("world");
}, 1000);
console.log("hello");

$ node debug examples/B/myscript.js
< debugger listening on port 5858
connecting... ok
break in examples/B/myscript.js:2
1 // myscript.js
2 x = 5;
3 setTimeout(function () {
4 debugger;
debug>

代碼中遇到debugger日戈;后會中斷询张,可以通過以下的命令進行debug。

命令 說明
cont 或者c浙炼,繼續(xù)執(zhí)行
next 或者n份氧,執(zhí)行到下一個斷點
step 或者s唯袄,步進到函數(shù)內(nèi)部
out 或者o,從函數(shù)內(nèi)部跳出
pause 暫停執(zhí)行
setBreakpoint() 或者sb()蜗帜,在當前行設置斷點
setBreakpoint(lineNo) 或者sb(lineNo)恋拷,在指定行設置斷點
setBreakpoint('fn()') 或者sb('fn()'),在函數(shù)體的第一個聲明處設置斷點
setBreakpoint('xxx.js') 或者sb('xxx.js')厅缺,在腳本文件的第一行設置斷點
clearBreakpoint 或者cb()蔬顾,清除斷點
backtrace 或者bt,打印當前執(zhí)行情況下的堆棧信息
list(5) 列出當前上下文前后5行的源代碼
watch(expr) 添加表達式到觀察者列表店归,進行觀察
unwatch(expr) 從觀察列表中移除對表達式的觀察
watchers 列出所有觀察的表達式和值
repl 打開調(diào)試的交互阎抒,用于執(zhí)行調(diào)試腳本的上下文

對于已經(jīng)啟動的程序如何debug

通過向程序發(fā)送SIGUSR1信號開啟調(diào)試,kill -s USR1 10093消痛,可以在瀏覽器上開啟調(diào)試且叁,使用工具Node Inspector。

安裝:npm install -g node-inspector

啟動的時候秩伞,直接node-inspector即可.....

當然逞带,使用node Inspector必須先啟用node進程的調(diào)試模式,也就是使用debug或者發(fā)送SIGUSR1給node進程來啟用調(diào)試模式纱新,啟動進程后展氓,調(diào)用Inspector工具,工具就與node進程建立了調(diào)試代理:

$ node-inspector
Node Inspector v0.5.0
info - socket.io started
Visit http://127.0.0.1:8080/debug?port=5858 to start debugging

剩下的就跟在IDE中調(diào)試代碼一樣了脸爱。

node調(diào)試的原理

在打開調(diào)試的時候遇汞,我們會看到這樣的輸出:

Debugger listening on port 5858

可以訪問下 http://localhost:5858

看到如下信息:

Type:connect
V8-Version:4.2.15.19
Protocol-Version:1
Embedding-Host:node v4.2.15
Content-Length:0

這個其實就是node調(diào)用呢內(nèi)建調(diào)試功能,并監(jiān)聽端口5858來使用調(diào)試命令簿废。我們還可以使用

node debug <URI>
node debug -p <pid>

來調(diào)用

如果我們使用 --debug 參數(shù)打開文件:
.

node --debug xxxx.js

此時空入,nodejs 不會進入到命令行模式,而是直接執(zhí)行代碼族檬,但是依然會開啟內(nèi)建調(diào)試功能歪赢,這就意味著我們具備了遠程調(diào)試 NodeJS 代碼的能力,使用 --debug 參數(shù)打開服務器的 nodejs 文件单料,然后通過:

node debug <服務器IP>:<調(diào)試端口埋凯,默認5858>

可以在本地遠程調(diào)試 nodejs 代碼。不過這里需要區(qū)分下 --debug 和 --debug-brk扫尖,前者會執(zhí)行完所有的代碼白对,一般是在監(jiān)聽事件的時候使用,而后者换怖,不會執(zhí)行代碼甩恼,需要等到外部調(diào)試接入后,進入代碼區(qū)。語言表述不會那么生動媳拴,讀者可以自行測試下。

默認端口號是 5858兆览,如果這個端口被占用屈溉,程序會遞增端口號,我們也可以指定端口:

node  node --debug-brk=8787 xxxx.js
Debugger listening on port 8787

不過在最新的node中抬探,調(diào)試的原理以及有所改變:

調(diào)試

可以看到直接推薦使用Inspector了子巾,因此,大家也就不必太過于在乎調(diào)試了小压,還是那句話:服務器上的debug比較麻煩线梗,還是通過良好的單元測試服務器日志進行錯誤排查。

性能調(diào)試

我們可以使用pm2這樣的檢測工具來檢測node怠益,現(xiàn)在我們想先分享一下產(chǎn)生問題的原因仪搔。或者說是什么問題造成了cpu或內(nèi)存達到瓶頸蜻牢,從而導致系統(tǒng)奔潰的呢烤咧?在菜譜負載過高,內(nèi)存突然飆升的時候又如何發(fā)現(xiàn)這些問題呢抢呆?

分析原因

cpu負載過重的原因

1.垃圾回收頻率過高煮嫌、量過大,也給也是因為內(nèi)存飆升造成的后遺癥
2.計算密集型的長循環(huán)抱虐,比如遍歷大量的文件昌阿、文件夾、大量的計算等

內(nèi)存飆升的原因

1.緩存恳邀,很多人把內(nèi)存當緩存用懦冰,比如session存在內(nèi)存中
2.閉包,作用域沒有被及時釋放轩娶,造成常駐內(nèi)存占用過多
3.生成者的生產(chǎn)能力和消費者的消費能力不匹配儿奶,例如數(shù)據(jù)庫已經(jīng)忙不過來了,但是Query隊列中還有大量請求堆積鳄抒。

解決手段

一般情況下闯捎,我們可以通過分析GC日志來查找問題:

問題 解釋
內(nèi)存飆升 特別是old space內(nèi)存的飆升,會直接導致GC的次數(shù)和時間增長
緩存增加 導致GC的時間增加许溅,無用遍歷過多(如果緩存增加(比如使用對象緩存了很多用戶信息)瓤鼻,GC 是不知道這些緩存死了還是活著的,他們會不停地查看這個對象贤重,以及這個對象中的子對象是否還存活茬祷,如果這個對象數(shù)特別大,那么 GC 遍歷的時間也會特別長并蝗。當我們進行密集型計算的時候祭犯,會產(chǎn)生很多中間變量秸妥,這些變量往往在 New Space 中就死掉了,那么 GC 也會在這里多次地進行 New Space 區(qū)域的垃圾回收沃粗。
密集型計算 導致GC new space次數(shù)增加粥惧。(也就是前邊說過的scavenage操作的cheney算法)

分析GC日志

在啟動程序的時候添加 --trace_gc 參數(shù),V8 在進行垃圾回收的時候最盅,會將垃圾回收的信息打印出來:

node --trace_gc aa.js
...
[94036]       68 ms: Scavenge 8.4 (42.5) -> 8.2 (43.5) MB, 2.4 ms [allocation failure].
[94036]       74 ms: Scavenge 8.9 (43.5) -> 8.9 (46.5) MB, 5.1 ms [allocation failure].
[94036] Increasing marking speed to 3 due to high promotion rate
[94036]       85 ms: Scavenge 16.1 (46.5) -> 15.7 (47.5) MB, 3.8 ms (+ 5.0 ms in 106 steps since last GC) [allocation failure].
[94036]       95 ms: Scavenge 16.7 (47.5) -> 16.6 (54.5) MB, 7.2 ms (+ 1.3 ms in 14 steps since last GC) [allocation failure].
[94036]      111 ms: Mark-sweep 23.6 (54.5) -> 23.2 (54.5) MB, 6.2 ms (+ 15.3 ms in 222 steps since start of marking, biggest step 0.3 ms) [GC interrupt] [GC in old space requested].
...

V8還提供了很多啟動選項:

啟動項 含義
–max-stack-size 設置棧大小
–v8-options 打印 V8 相關命令
–trace-bailout 查找不能被優(yōu)化的函數(shù)突雪,重寫
–trace-deopt 查找不能優(yōu)化的函數(shù)

這些啟動項都可以讓我們查看 V8 在執(zhí)行時的各種 log 日志,對于排查隱晦問題比較有用

另外涡贱,通過 Profile 可以找到具體函數(shù)在整個程序中的執(zhí)行時間和執(zhí)行時間占比咏删,從而分析到具體的代碼問題,V8 也提供了 Profile 日志導出:

$ node --prof test.js
//執(zhí)行命令之后问词,會在該目錄下產(chǎn)生一個 *-v8.log 的日志文件督函,我們可以安裝一個日志分析工具 tick:
npm install tick -g
node-tick-processor *-v8.log
[Top down (heavy) profile]:
  Note: callees occupying less than 0.1% are not shown.

  inclusive      self           name
  ticks   total  ticks   total
    426   36.7%      0    0.0%  Function: ~<anonymous> node.js:27:10
    426   36.7%      0    0.0%    LazyCompile: ~startup node.js:30:19
    410   35.3%      0    0.0%      LazyCompile: ~Module.runMain module.js:499:26
    409   35.2%      0    0.0%        LazyCompile: Module._load module.js:273:24
    407   35.1%      0    0.0%          LazyCompile: ~Module.load module.js:345:33
    406   35.0%      0    0.0%            LazyCompile: ~Module._extensions..js module.js:476:37
    405   34.9%      0    0.0%              LazyCompile: ~Module._compile module.js:378:37
...

解決問題

對于內(nèi)存當做緩存的,我們可以引入外部緩存如redis等戏售,對于閉包侨核,我們可以人為控制閉包的釋放,對于大文件灌灾,我們可以將其分解為小文件迭代的去讀取搓译,總之,方法多多锋喜,大家可以繼續(xù)探討

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末些己,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嘿般,更是在濱河造成了極大的恐慌段标,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炉奴,死亡現(xiàn)場離奇詭異逼庞,居然都是意外死亡,警方通過查閱死者的電腦和手機瞻赶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門赛糟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人砸逊,你說我怎么就攤上這事璧南。” “怎么了师逸?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵司倚,是天一觀的道長。 經(jīng)常有香客問我,道長动知,這世上最難降的妖魔是什么皿伺? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮盒粮,結(jié)果婚禮上心傀,老公的妹妹穿的比我還像新娘。我一直安慰自己拆讯,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布养叛。 她就那樣靜靜地躺著种呐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弃甥。 梳的紋絲不亂的頭發(fā)上爽室,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音淆攻,去河邊找鬼阔墩。 笑死,一個胖子當著我的面吹牛瓶珊,可吹牛的內(nèi)容都是我干的啸箫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼伞芹,長吁一口氣:“原來是場噩夢啊……” “哼忘苛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唱较,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤扎唾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后南缓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胸遇,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年汉形,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸镊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡获雕,死狀恐怖薄腻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情届案,我是刑警寧澤庵楷,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響尽纽,放射性物質(zhì)發(fā)生泄漏咐蚯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一弄贿、第九天 我趴在偏房一處隱蔽的房頂上張望春锋。 院中可真熱鬧,春花似錦差凹、人聲如沸期奔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呐萌。三九已至,卻和暖如春谊娇,著一層夾襖步出監(jiān)牢的瞬間肺孤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工济欢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赠堵,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓法褥,卻偏偏與公主長得像茫叭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子半等,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評論 25 707
  • 這兩天鼓搗了一下node項目杂靶,記錄一下node的調(diào)試方法。 前端開發(fā)的調(diào)試可以用打日志(console.log)或...
    采姑娘的大白菜閱讀 16,166評論 2 9
  • 有點霧酱鸭,有點雨吗垮,有點蒙蒙細雨
    謂言閱讀 192評論 0 0
  • 作者 劉超奇 日內(nèi) 普通有著窗戶的客廳 一男穿著短褲背心躲在客廳的陰涼角 男: 熱烁登,簡直烈日灼身。這個世界怎么變得...
    貓云七閱讀 380評論 1 1
  • 在美麗的法國——巴黎蔚舀,是每個人愛戀的地方饵沧。 ...
    ceb41f56410c閱讀 155評論 1 1