日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Node.js 应用故障排查手册 —— 正确打开 Chrome devtools

發(fā)布時(shí)間:2024/8/23 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Node.js 应用故障排查手册 —— 正确打开 Chrome devtools 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

楔子

前面的預(yù)備章節(jié)中我們大致了解了如何在服務(wù)器上的 Node.js 應(yīng)用出現(xiàn)問(wèn)題時(shí),從常規(guī)的錯(cuò)誤日志、系統(tǒng)/進(jìn)程指標(biāo)以及兜底的核心轉(zhuǎn)儲(chǔ)這些角度來(lái)排查問(wèn)題。這樣就引出了下一個(gè)問(wèn)題:我們知道進(jìn)程的 CPU/Memory 高,或者拿到了進(jìn)程 Crash 后的核心轉(zhuǎn)儲(chǔ),要如何去進(jìn)行分析定位到具體的 JavaScript 代碼段。

其實(shí) Chrome 自帶的 Devtools,對(duì)于 JavaScript 代碼的上述 CPU/Memory 問(wèn)題有著很好的原生解析展示,本節(jié)會(huì)給大家做一些實(shí)用功能和指標(biāo)的介紹(基于 Chrome v72,不同的版本間使用方式存在差異)。

本書首發(fā)在 Github,倉(cāng)庫(kù)地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云棲社區(qū)會(huì)同步更新。

CPU 飆高問(wèn)題

I. 導(dǎo)出 JS 代碼運(yùn)行狀態(tài)

當(dāng)我們通過(guò)第一節(jié)中提到的系統(tǒng)/進(jìn)程指標(biāo)排查發(fā)現(xiàn)當(dāng)前的 Node.js 應(yīng)用的 CPU 特別高時(shí),首先我們需要去通過(guò)一些方式將當(dāng)前 Node.js 應(yīng)用一段時(shí)間內(nèi)的 JavaScript 代碼運(yùn)行狀況 Dump 出來(lái),這樣子才能分析知道 CPU 高的原因。幸運(yùn)的是,V8 引擎內(nèi)部實(shí)現(xiàn)了一個(gè) CPU Profiler 能夠幫助我們完成一段時(shí)間內(nèi) JS 代碼運(yùn)行狀態(tài)的導(dǎo)出,目前也有不少成熟的模塊或者工具來(lái)幫我們完成這樣的操作。

v8-profiler?是一個(gè)老牌的 Node.js 應(yīng)用性能分析工具,它可以很方便地幫助開發(fā)者導(dǎo)出 JS 代碼地運(yùn)行狀態(tài),我們可以在項(xiàng)目目錄執(zhí)行如下命令安裝此模塊:

npm install v8-profiler --save

接著可以在代碼中按照如下方式獲取到?5s?內(nèi)地 JS 代碼運(yùn)行狀態(tài):

'use strict';const v8Profiler = require('v8-profiler'); const title = 'test'; v8Profiler.startProfiling(title, true); setTimeout(() => {const profiler = v8Profiler.stopProfiling(title);profiler.delete();console.log(profiler); }, 5000);

那么我們可以看到,v8-profiler 模塊幫我導(dǎo)出的代碼運(yùn)行狀態(tài)實(shí)際上是一個(gè)很大的 JSON 對(duì)象,我們可以將這個(gè) JSON 對(duì)象序列化為字符串后存儲(chǔ)到文件:test.cpuprofile?。注意這里的文件名后綴必須為?.cpuprofile?,否則 Chrome devtools 是不識(shí)別的。

注意:v8-profiler 目前也處于年久失修的狀態(tài)了,在 Node.js 8 和 Node.js 10 上已經(jīng)無(wú)法正確編譯安裝了,如果你在 8 或者 10 的項(xiàng)目中想進(jìn)行使用,可以試試看?v8-profiler-next。

II. 分析 CPU Profile 文件

借助于 v8-profiler 拿到我們的 Node.js 應(yīng)用一段時(shí)間內(nèi)的 JS 代碼運(yùn)行狀態(tài)后,我們可以將其導(dǎo)入 Chrome devtools 中進(jìn)行分析展示。

在 Chrome 72 中,分析我們 Dump 出來(lái)的 CPU Profile 的方法已經(jīng)和之前有所不同了,默認(rèn)工具欄中也不會(huì)展示 CPU Profile 的分析頁(yè)面,我們需要通過(guò)點(diǎn)擊工具欄右側(cè)的?更多?按鈕,然后選擇?More tools?->?JavaScript Profiler?來(lái)進(jìn)入到 CPU 的分析頁(yè)面,如下圖所示:

選中?JavaScript Profiler?后,在出現(xiàn)的頁(yè)面上點(diǎn)擊?Load?按鈕,然后將剛才保存得到的?test.cpuprofile?文件加載進(jìn)來(lái),就可以看到 Chrome devtools 的解析結(jié)果了:

這里默認(rèn)的視圖是 Heavy 視圖,在這個(gè)視圖下,Devtools 會(huì)按照對(duì)你的應(yīng)用的影響程度從高到低,將這些函數(shù)列舉出來(lái),點(diǎn)擊展開能夠看到這些列舉出來(lái)的函數(shù)的全路徑,方便你去代碼中對(duì)應(yīng)的位置進(jìn)行排查。這里解釋兩個(gè)比較重要的指標(biāo),以便讓大家能更有針對(duì)性地進(jìn)行排查:

  • Self Time:?此函數(shù)本身代碼段執(zhí)行地時(shí)間(不包含任何調(diào)用)
  • Total Time:?此函數(shù)包含了其調(diào)用地其它函數(shù)總共的執(zhí)行時(shí)間

像在上述地截圖例子中,ejs 模塊在線上都應(yīng)該開啟了緩存,所以 ejs 模塊的?compile?方法不應(yīng)該出現(xiàn)在列表中,這顯然是一個(gè)非??梢傻男阅軗p耗點(diǎn),需要我們?nèi)フ归_找到原因。

除了 Heavy 視圖,Devtools 實(shí)際上還給我們提供了火焰圖來(lái)進(jìn)行更多維度的展示,點(diǎn)擊左上角可以切換:

火焰圖按照我們的 CPU 采樣時(shí)間軸進(jìn)行展示,那么在這里我們更容易看到我們的 Node.js 應(yīng)用在采樣期間 JS 代碼的執(zhí)行行為,新增的兩個(gè)指標(biāo)這邊也給大家解釋一下其含義:

  • Aggregated self time:?在 CPU 采樣期間聚合后的此函數(shù)本身代碼段的執(zhí)行總時(shí)間(不包含其他調(diào)用)
  • Aggregated total time:?在 CPU 采樣期間聚合后的此函數(shù)包含了其調(diào)用地其它函數(shù)總共的執(zhí)行總時(shí)間

綜上,借助于 Chrome devtools 和能夠?qū)С霎?dāng)前 Node.js 應(yīng)用 Javascript 代碼運(yùn)行狀態(tài)的模塊,我們已經(jīng)可以比較完備地對(duì)應(yīng)用服務(wù)異常時(shí),排查定位到相應(yīng)的 Node.js 進(jìn)程 CPU 很高的情況進(jìn)行排查和定位分析了。在生產(chǎn)實(shí)踐中,這部分的 JS 代碼的性能的分析往往也會(huì)用到新項(xiàng)目上線前的性能壓測(cè)中,有興趣的同學(xué)可以更深入地研究下。

內(nèi)存泄漏問(wèn)題

I. 判斷是否內(nèi)存泄漏

在筆者的經(jīng)歷中,內(nèi)存泄漏問(wèn)題是 Node.js 在線上運(yùn)行時(shí)出現(xiàn)的問(wèn)題種類中的重災(zāi)區(qū)。尤其是三方庫(kù)自身的 Bug 或者開發(fā)者使用不當(dāng)引起的內(nèi)存泄漏,會(huì)讓很多的 Node.js 開發(fā)者感到束手無(wú)策。本節(jié)首先向讀者介紹下,什么情況下我們的應(yīng)用算是有很大的可能在發(fā)生內(nèi)存泄漏呢?

實(shí)際上判斷我們的線上 Node.js 應(yīng)用是否有內(nèi)存泄漏也非常簡(jiǎn)單:借助于大家各自公司的一些系統(tǒng)和進(jìn)程監(jiān)控工具,如果我們發(fā)現(xiàn) Node.js 應(yīng)用的總內(nèi)存占用曲線?處于長(zhǎng)時(shí)間的只增不降?,并且堆內(nèi)存按照趨勢(shì)突破了?堆限制的 70%? 了,那么基本上應(yīng)用?很大可能?產(chǎn)生了泄漏。

當(dāng)然事無(wú)絕對(duì),如果確實(shí)應(yīng)用的訪問(wèn)量(QPS)也在一直增長(zhǎng)中,那么內(nèi)存曲線只增不減也屬于正常情況,如果確實(shí)因?yàn)?QPS 的不斷增長(zhǎng)導(dǎo)致堆內(nèi)存超過(guò)堆限制的 70% 甚至 90%,此時(shí)我們需要考慮的擴(kuò)容服務(wù)器來(lái)緩解內(nèi)存問(wèn)題。

II. 導(dǎo)出 JS 堆內(nèi)存快照

如果確認(rèn)了 Node.js 應(yīng)用出現(xiàn)了內(nèi)存泄漏的問(wèn)題,那么和上面 CPU 的問(wèn)題一樣,我們需要通過(guò)一些辦法導(dǎo)出 JS 內(nèi)存快照(堆快照)來(lái)進(jìn)行分析。V8 引擎同樣在內(nèi)部提供了接口可以直接將分配在 V8 堆上的 JS 對(duì)象導(dǎo)出來(lái)供開發(fā)者進(jìn)行分析,這里我們采用?heapdump?這個(gè)模塊,首先依舊是執(zhí)行如下命令進(jìn)行安裝:

npm install heapdump --save

接著可以在代碼中按照如下方法使用此模塊:

'use sytrict';const heapdump = require('heapdump'); heapdump.writeSnapshot('./test' + '.heapsnapshot');

這樣我們就在當(dāng)前目錄下得到了一個(gè)堆快照文件:test.heapsnapshot?,用文本編輯工具打開這個(gè)文件,可以看到其依舊是一個(gè)很大的 JSON 結(jié)構(gòu),同樣這里的堆快照文件后綴必須為?.heapsnapshot?,否則 Chrome devtools 是不識(shí)別的。

III. 分析堆快照

在 Chrome devtools 的工具欄中選擇?Memory?即可進(jìn)入到分析頁(yè)面,如下圖所示:

然后點(diǎn)擊頁(yè)面上的?Load?按鈕,選擇我們剛才生成 test.heapsnapshot 文件,就可以看到分析結(jié)果,如下圖所示:

默認(rèn)的視圖其實(shí)是一個(gè) Summary 視圖,這里的?Constructor?和我們編寫 JS 代碼時(shí)的構(gòu)造函數(shù)并無(wú)不同,都是指代此構(gòu)造函數(shù)創(chuàng)建的對(duì)象,新版本的 Chrome devtools 中還在構(gòu)造函數(shù)后面增加?* number?的信息,它代表這個(gè)構(gòu)造函數(shù)創(chuàng)建的實(shí)例的個(gè)數(shù)。

實(shí)際上在堆快照的分析視圖中,有兩個(gè)非常重要的概念需要大家去理解,否則很可能拿到堆快照看著分析結(jié)果也無(wú)所適從,它們是?Shallow Size?和?Retained Size?,要更好地去理解這兩個(gè)概念,我們需要先了解?支配樹。首先我們看如下簡(jiǎn)化后的堆快照描述的內(nèi)存關(guān)系圖:

這里的 1 為根節(jié)點(diǎn),即 GC 根,那么對(duì)于對(duì)象 5 來(lái)說(shuō),如果我們想要讓對(duì)象 5 回收(即從 GC 根不可達(dá)),僅僅去掉對(duì)象 4 或者對(duì)象 3 對(duì)于對(duì)象 5 的引用是不夠的,因?yàn)轱@然從根節(jié)點(diǎn) 1 可以分別從對(duì)象 3 或者對(duì)象 4 遍歷到對(duì)象 5。因此我們只有去掉對(duì)象 2 才能將對(duì)象 5 回收,所以在上面這個(gè)圖中,對(duì)象 5 的直接支配者是對(duì)象 2。照著這個(gè)思路,我們可以通過(guò)一定的算法將上述簡(jiǎn)化后的堆內(nèi)存關(guān)系圖轉(zhuǎn)化為支配樹:

對(duì)象 1 到對(duì)象 8 間的支配關(guān)系描述如下:

  • 對(duì)象 1 支配對(duì)象 2
  • 對(duì)象 2 支配對(duì)象 3 、4 和 5
  • 對(duì)象 4 支配對(duì)象 6
  • 對(duì)象 5 支配對(duì)象 7
  • 對(duì)象 6 支配對(duì)象 8

好了,到這里我們可以開始解釋什么是 Shallow Size 和 Retained Size 了,實(shí)際上對(duì)象的?Shallow Size 就是對(duì)象自身被創(chuàng)建時(shí),在 V8 堆上分配的大小,結(jié)合上面的例子,即對(duì)象 1 到 8 自身的大小。對(duì)象的?Retained Size 則是把此對(duì)象從堆上拿掉,則 Full GC 后 V8 堆能夠釋放出的空間大小。同樣結(jié)合上面的例子,支配樹的葉子節(jié)點(diǎn)對(duì)象 3、對(duì)象 7 和對(duì)象 8 因?yàn)闆]有任何直接支配對(duì)象,因此其 Retained Size 等于其 Shallow Size。

將剩下的非葉子節(jié)點(diǎn)可以一一展開,為了篇幅描述方便,SZ_5表示對(duì)象 5 的 Shallow Size,RZ_5 表示對(duì)象 5 的 Retained Size,那么可以得到如下結(jié)果:

  • 對(duì)象 3 的 Retained Size:RZ_3 = SZ_3
  • 對(duì)象 7 的 Retained Size:RZ_7 = SZ_7
  • 對(duì)象 8 的 Retained Size:RZ_8 = SZ_8
  • 對(duì)象 6 的 Retained Size:RZ_6 = SZ_6 + RZ_8 = SZ_6 + SZ_8
  • 對(duì)象 5 的 Retained Size:RZ_5 = SZ_5 + RZ_7 = SZ_5 + SZ_7
  • 對(duì)象 4 的 Retained Size:RZ_4 = SZ_4 + RZ_6 = SZ_4 + SZ_6 + SZ_8
  • 對(duì)象 2 的 Retained Size:RZ_2 = SZ_2 + RZ_3 + RZ_4 + RZ_5 = SZ_2 + SZ_3 + SZ_4 + SZ_5 + SZ_6 + SZ_7 + SZ_8
  • GC 根 1 的 Retained Size:RZ_1 = SZ_1 + RZ_2 =?SZ_1 +?SZ_2 + RZ_3 + RZ_4 + RZ_5 = SZ_2 + SZ_3 + SZ_4 + SZ_5 + SZ_6 + SZ_7 + SZ_8

這里可以發(fā)現(xiàn),GC 根的 Retained Size 等于堆上所有從此根出發(fā)可達(dá)對(duì)象的 Shallow Size 之和,這和我們的理解預(yù)期是相符合的,畢竟將 GC 根從堆上拿掉的話,原本就應(yīng)當(dāng)將從此根出發(fā)的所有對(duì)象都清理掉。

理解了這一點(diǎn),回到我們最開始看到的默認(rèn)總覽視圖中,正常來(lái)說(shuō),可能的泄漏對(duì)象往往其 Retained Size 特別大,我們可以在窗口中依據(jù) Retained Size 進(jìn)行排序來(lái)對(duì)那些占據(jù)了堆空間絕大部分的對(duì)象進(jìn)行排查:

假如確認(rèn)了可疑對(duì)象,Chrome devtools 中也會(huì)給你自動(dòng)展開方便你去定位到代碼段,下面以 NativeModule 這個(gè)構(gòu)造器生成的對(duì)象 vm 為例:

這里上半部分是順序的引用關(guān)系,比如 NativeModule 實(shí)例 @45655 的 exports 屬性指向了對(duì)象 @45589,filename 屬性則指向一個(gè)字符串 "vm.js";下半部分則是反向的引用關(guān)系:NativeModule 實(shí)例 @13021 的 _cache 屬性指向了 Object 實(shí)例 @41103,而?Object 實(shí)例 @41103 的 vm 屬性指向了?NativeModule 實(shí)例 @45655。

如果對(duì)這部分展示圖表比較暈的可以仔細(xì)看下上面的例子,因?yàn)檎业娇梢傻男孤?duì)象,結(jié)合上圖能看到此對(duì)象下的屬性和值及其父引用關(guān)系鏈,絕大部分情況下我們就可以定位到生成可疑對(duì)象的 JS 代碼段了。

實(shí)際上除了默認(rèn)的 Summary 視圖,Chrome devtools 還提供了 Containment 和 Statistics 視圖,這里再看下?Containment 視圖,選擇堆快照解析頁(yè)面的左上角可以進(jìn)行切換,如下圖所示:

這個(gè)視圖實(shí)際上是堆快照解析出來(lái)的內(nèi)存關(guān)系圖的直接展示,因此相比 Summary 視圖,從這個(gè)視圖中直接查找可疑的泄漏對(duì)象相對(duì)比較困難。

結(jié)尾

Chrome devtools 實(shí)際上是非常強(qiáng)大的一個(gè)工具,本節(jié)也只是僅僅介紹了對(duì) CPU Profile 和堆快照解析能力的介紹和常用視圖的使用指南,如果你仔細(xì)閱讀了本節(jié)內(nèi)容,面對(duì)服務(wù)器上定位到的 Node.js 應(yīng)用 CPU 飆高或者內(nèi)存泄漏這樣的問(wèn)題,想必就可以做到心中有數(shù)不慌亂了。


原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

總結(jié)

以上是生活随笔為你收集整理的Node.js 应用故障排查手册 —— 正确打开 Chrome devtools的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。