前一節中咱們藉助於 Chrome devtools 實現了對線上 Node.js 應用的 CPU/Memory 問題的排查定位,可是在實際生產實踐中,你們會發現 Chrome devtools 更加偏向本地開發模式,由於顯然 Chrome devtools 不會負責去生成分析問題所須要的 Dump 文件,這意味着開發者還得額外在線上項目中設置好 v8-profiler 和 heapdump 這樣的工具,而且經過額外實現的服務來可以去對線上運行的項目進行實時的狀態導出。html
加上實際上預備章中除了 CPU/Memory 的問題,咱們還會遇到一些須要分析錯誤日誌、磁盤和核心轉儲文件等才能定位問題的情況,所以在這些場景下,僅僅靠 Chrome devtools 顯然會有一些力不從心。正是爲了解決廣大 Node.js 開發者的這些痛點,咱們在這裏推薦你們在使用 Node.js 性能平臺,即原來的 AliNode,它已經在阿里巴巴集團內部承載了幾乎全部的 Node.js 應用線上運行監控和問題排查,所以你們能夠放心在生產環境部署使用。node
本節將從 Node.js 性能平臺 的設計架構、核心能力以及最佳實踐等角度,幫助開發者更好地使用這一工具來解決前面提到的異常指標分析和線上 Node.js 應用故障定位。git
本書首發在 Github,倉庫地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,雲棲社區會同步更新。github
Node.js 性能平臺其實簡單的說由三部分組成:雲控制檯 + AliNode runtime + Agenthub,以下圖所示:npm
具體的部署步驟能夠查看官方文檔:自助式部署 Runtime。藉助於 Node.js 性能平臺的整套解決方案,咱們能夠很方便地實現預備章節中提到的絕大部分異常指標的告警分析的能力。在生產實踐過程當中,實際上在筆者看來,Node.js 性能平臺解決方案其實僅僅是提供了三個最核心卻也是最有效的能力:數組
換言之,Node.js 性能平臺做爲一個產品自己功能也在不斷迭代新增修改中,可是以上的三個核心能力必定是第一優先級保障的,其它邊邊角角的功能則相對來講響應優先級沒有那麼高。服務器
實際上咱們也理解做爲使用平臺的開發者但願能在一個地方看到 Node.js 線上應用從底層到業務層的全部細節,然而我我的感受不一樣的工具都有應該有其核心的能力輸出,不少時候不斷作加法容易讓產品自己定位模糊化以及泛而不精,Node.js 性能平臺實際上始終在致力於讓本來線上黑盒的運行時狀態,能更加直觀地反饋給開發者,讓 Node.js 應用的開發者面對一些偏向底層的線上疑難雜症可以再也不無所適從。架構
線上應用的告警其實是一種自我發現問題的保護機制,若是沒有告警能力,那每次都會等到問題暴露到用戶側致使其反饋才能發現問題,這顯然對用戶體驗很是的不友好。ide
所以部署完成一個項目後,開發者首先須要去配置合適的告警,而在咱們的生產實踐中,線上問題經過錯誤日誌、Node.js 進程 CPU/Memory 的分析、核心轉儲(Core dump)的分析以及磁盤分析可以得出結論,所以咱們須要的基本的告警策略也是源自以上五個部分。幸運的是平臺已經給咱們預設好了這些告警,你們只須要選擇一下便可完整這裏的告警配置,以下圖所示:函數
在 Node.js 性能平臺 的告警頁面上有 快速添加規則,點開選中後會自動生成告警規則的閾值表達式模板和報警說明模板,咱們能夠按照項目實際監控需求進行修改,好比想要對 Node.js 進程的堆內存進行監控,能夠選中 Memory 預警 選項,以下圖所示:
此時點擊 添加報警項 即完整了對進程堆內存的告警,而且將出現告警時須要點擊 通知設置->添加到聯繫人列表 來添加的聯繫人加入此條規則,以下圖所示:
那麼在例子中的這條默認的規則裏,當咱們的 Node.js 進程分配的堆內存超過堆上線的 80%(默認 64 位機器上堆上限是 1.4G)時會觸發短信通知到配置綁定到此條規則的聯繫人。
實際上快速添加規則列表中給你們提供的是最多見的一些預配置好的告警策略,若是這些尚不能知足你的需求,更多定製化的自定義的服務告警策略配置方法能夠看官方文檔 報警設置。而且除了短信告警,也支持釘釘機器人推送告警消息到羣,方便羣發感知線上 Node.js 應用態勢。
按照 I 節中配置完成合適的告警規則後,那麼當收到告警短信時就能夠按照策略類型進行對應的分析了。本節將按照預備節中比較常見的五大類問題來逐一講解。
a. 磁盤監控
這個是比較好處理的問題,在快速添加的規則裏實際上咱們會在服務器的磁盤使用超過 85% 時進行告警,那麼收到磁盤告警後,能夠鏈接到服務器,使用以下命令查看那個目錄佔用比較高:
sudo du -h --max-depth=1 /
找到佔比比較高的目錄和文件後,看看是否須要備份後刪除來釋放出磁盤空間。
b. 錯誤日誌
收到特定的錯誤日誌告警後,只須要去對應的項目的 Node.js 性能平臺 控制檯找到問題 實例 去查看其 異常日誌 便可,以下圖所示:
這裏會按照錯誤類型進行規整,你們能夠結合展現的錯誤棧信息來進行對應的問題定位。注意這裏的錯誤日誌文件須要你在部署 Agenthub 的時候寫入配置文件,詳細能夠參見文檔 配置和啓動 Agenthub 一節中的 詳細配置 內容。
c. 進程 CPU 高
終於到了前一節中藉助 v8-profiler 導出 CPU Profile 文件再使用 Chrome devtools 進行分析的異常類型了。那麼在 Node.js 性能平臺 的整套解決方案下,咱們並不須要額外的去依賴相似 v8-profiler 這樣的第三方庫來實現進程狀態的導出,與此相對的,當咱們收到 Node.js 應用進程的 CPU 超過咱們設置的閾值告警時,咱們只須要在控制檯對應的 實例 點擊 CPU Profile 按鈕便可:
默認會給抓取的進程生成 3 分鐘的 CPU Profile 文件,等到結束後生成的文件會顯示在 文件 頁面:
此時點擊 轉儲 便可上傳到雲端以供在線分析展現了,以下圖所示:
這裏能夠看到有兩個 分析 按鈕,其實第二個下標帶有 (devtools) 的分析按鈕實際上就是前一節中提到的 Chrome devtools 分析,這裏再也不重複講解了,若是有遺忘的同窗能夠再去回顧下本大章前一節的內容。咱們重點看下第一個 AliNode 定製的分析,點擊第一個分析按鈕後,能夠在新頁面看到以下所示內容:
這裏其實也是火焰圖,但與 Chrome devtools 提供的火焰圖不同的地方在於,這裏是將抓取的 3 分鐘內的 JS 函數執行進行了聚合展現出來的火焰圖,在一些存在屢次執行同一個函數(可能每次執行很是短)的狀況下,聚合後的火焰圖能夠很方便的幫助咱們找到代碼的執行瓶頸來進行對應的優化。
值得一提的是,若是你使用的 AliNode runtime 版本在 v3.11.4 或者 v4.2.1 以上(包含這兩個版本)的話,當你的應用出現類死循環問題,好比因爲異常的用戶參數致使的正則回溯(即執行完這個正則要十幾年,相似於 Node.js 進程發生了死循環)這類問題時,能夠經過抓取 CPU Profile 文件來很方便地定位到問題代碼,詳細信息有興趣的同窗能夠看下 Node.js 性能平臺支持死循環和正則攻擊定位。
d. 內存泄漏
與 CPU 高的問題同樣,當咱們收到 Node.js 應用進程的堆內存佔據堆上限的比率超過咱們設置的閾值時,咱們也不須要相似 heapdump 這樣的第三方模塊來導出堆快照進行分析,咱們仍是在控制檯對應的 實例 點擊 堆快照 按鈕便可生成對應 Node.js 進程的堆快照:
生成的堆快照文件一樣會顯示在 文件 列表頁面,點擊 轉儲 將堆快照上傳至雲端以供接下來的分析:
與上面同樣,下標帶有 (devtools) 的分析按鈕仍是前一節中提到的 Chrome devtools 分析,這裏仍是着重解析下 AliNode 定製的第一個分析按鈕,點擊後新頁面以下圖所示:
首先解釋下上面的總覽欄目的內容信息:
這部分的信息旨在給你們一個概覽,部分信息須要深刻解讀堆快照才能完全理解,若是你實在沒法理解其中的幾個概覽指標信息,其實也無傷大雅,由於這並不影響咱們定位問題代碼。
簡單瞭解了概覽信息的含義後,接着咱們來看對於定位 Node.js 應用代碼段很是重要的信息,第一個是默認展現的 可疑點 信息,上圖中的內容表示 @15249 這個對象佔據了堆空間 97.41% 的內存,那麼它極可能就是一個泄漏對象,這裏又存在兩種可能:
要判斷是哪種狀況,以及追蹤到對應的代碼段,咱們須要點擊圖中的 簇視圖 連接進行進一步觀察:
這裏繼續解釋下什麼是簇視圖,簇視圖其實是支配樹的一個別名,也就是這個視圖下咱們看到的正是前面一節中提到的從可疑泄漏對象出發的支配樹視圖,它的好處是,在這個視圖下,父節點的 Retained Size 能夠直接由其子節點的 Retained Size 累加後再加上父節點自身的 Shallow Size 獲得,換言之,在這個視圖下咱們層層展開便可以看到可疑泄漏對象的內存究竟被哪些子節點佔用了。
而且結合前一節的支配樹描述,咱們能夠知道支配樹下的父子節點關係,並不必定是真正的堆上空間內的對象父子關係,可是對於那些支配樹下父子關係在真正的堆空間內也存在父子節點關係的簇節點,咱們將真正的 邊 也用淺紫色標識出來,這部分的 邊 信息對於咱們映射到真正的代碼段很是有幫助。在這個簡單的例子中,咱們能夠很清晰的看到可疑泄漏對象 @15249 其實是下屬的 test-alinode.js 中存在一個 array 變量,其中存儲了四個 45.78 兆的數組致使的問題,這樣就找到了問題代碼能夠進行後續優化。
而在實際生產環境的堆快照分析下,不少狀況下簇視圖下的父子關係在真實的堆空間中並不存在,那麼就不會有這些紫色的邊信息展現,這時候咱們想要知道可疑泄漏對象如何經過 JavaScript 生成的對象間引用關係引用到後面真正佔據掉堆空間的對象(好比上圖中的 40 多兆的 Array 對象),咱們能夠點擊 可疑節點自身的地址連接 :
這樣就進入到以此對象爲起點的堆空間內真正的對象引用關係視圖 Search 視圖:
這個視圖由於反映的是堆空間內各個 Heap Object 之間真正的引用鏈接關係,所以父對象的 Retained Size 並不能直接由子節點的 Retained Size 累加獲取,如上圖紅框內的內容,顯然這裏的三個子節點 Retained Size 累加已經超過 100%,這也是 Search 視圖和簇視圖很大的不一樣點。藉助於 Search 視圖,咱們能夠根據其內反映出來的對象和邊之間的關係來定位可疑泄漏對象具體是在咱們的 JavaScript 代碼中的哪一部分生成。
其實看到這邊,一些讀者應該意識到了這裏的 Search 視圖實際上對應的就是前一節中提到的 Chrome devtools 的 Containment 視圖,只不過這裏的起始點是咱們選中的對象自己罷了。
最後就是須要提一下 Retainers 視圖,它和前一節中提到的 Chrome devtools 中解析堆快照展現結果裏面的 Retainers 含義是一致的,它表示對象的父引用關係鏈,咱們能夠來看下:
這裏 globa@1279 對象的 clearImmediate 屬性指向 timers.js()@15325,而 timers.js()@15325 的 context 屬性指向了可疑的泄漏對象 system / Context@15249。
在絕大部分的狀況下,經過結合 Search 視圖 和 Retainers 視圖 咱們能夠定位到指定對象在 JavaScript 代碼中的生成位置,而 簇視圖 下咱們又能夠比較方便的知道堆空間被哪些對象佔據掉了,那麼綜合這兩部分的信息,咱們就能夠實現對線上內存泄漏的問題進行分析和代碼定位了。
e. 出現核心轉儲
最後就是收到服務器生成核心轉儲文件(Core dump 文件)的告警了,這表示咱們的進程已經出現了預期以外的 Crash,若是你的 Agenthub 配置正常的話,在 文件 -> Coredump 文件 頁面會自動將生成的核心轉儲文件信息展現出來:
和以前的步驟相似,咱們想要看到服務端分析和結果展現,首先須要將服務器上生成的核心轉儲文件轉儲到雲端,可是與以前的 CPU Profile 和堆快照的轉儲不同的地方在於,核心轉儲文件的分析須要咱們提供對應 Node.js 進程的啓動執行文件,即 AliNode runtime 文件,這裏簡化處理爲只須要設置 Runtime 版本便可:
點擊 設置 runtime 版本 便可進行設置,格式爲 alinode-v{x}.{y}.{z}
的形式,好比 alinode-v3.11.5,版本會進行校驗,請務必填寫你的應用真實在使用的 AliNode runtime 版本。版本填寫完成後,咱們就能夠點擊 轉儲 按鈕進行文件轉儲到雲端的操做了:
顯然對於核心轉儲文件來講,Chrome devtools 是沒有提供解析功能的,因此這裏只有一個 AliNode 定製的分析按鈕,點擊這個 分析 按鈕,便可以看到結果:
這裏第一欄的概覽信息看文字描述就能理解其含義,因此這裏就再也不多作解釋了,咱們來看下比較重要的默認視圖 BackTrace 信息視圖,此視圖下展現的其實是 Node.js 應用在 Crash 時刻的線程信息,許多開發者認爲 Node.js 是單線程的運行模型,其實這句話也不是徹底錯誤,更準確的說法是 單主 JavaScript 工做線程,由於實際上 Node.js 還會開啓一些後臺線程來處理諸如 GC 裏的部分任務。
絕大部分的狀況下,應用的 Crash 都是由 JavaScript 工做線程引起的,所以咱們須要關注的也僅僅是這個線程,這裏顯然 BackTrace 信息視圖中將 JavaScript 工做線程作了標紅和置頂處理,展開後能夠看到 Node.js 應用 Crash 那一刻的錯誤堆棧信息:
由於就算在 JavaScript 的工做線程中,也會存在 Native C/C++ 代碼的穿透,可是在問題排查中咱們每每只須要去看一樣標紅的 JavaScript 棧信息便可,像在這個簡單的例子中,顯然 Crash 是由於視圖去啓動一個不存在的 JS 文件致使的。
值得一提的是,核心轉儲文件的分析功能很是的強大,由於在預備節中咱們提到其生成的途徑除了 Node.js 應用 Crash 的時候由系統內核控制輸出外,還能夠由 gcore
這樣的命令手動強制輸出,而本小節咱們又看到核心轉儲文件的分析實際上能夠看到此刻的 JavaScript 棧信息以及其入參,結合這兩點,咱們能夠在線上出現 CPU Profile 一節中提到的類死循環問題時直接採用 gcore
生成核心轉儲文件,而後上傳至平臺雲端進行分析,這樣不只能夠看到咱們的 Node.js 應用是阻塞在哪一行的 JavaScript 代碼,甚至引起阻塞的參數咱們也能完整獲取到,這對本地復現定位問題的幫助無疑是無比巨大的。
本節其實給你們介紹了 Node.js 性能平臺 的整套面向 Node.js 應用開發的監控、告警、分析和定位問題的解決方案的架構和最佳實踐,但願能讓你們對平臺的能力和如何更好地結合自身項目進行使用有一個總體的理解。
限於篇幅,最佳實踐中的 CPU Profile、堆快照和核心轉儲文件的分析例子都很是的簡單,這部分的內容也更多的是旨在幫助你們理解平臺提供的工具如何使用以及其分析結果展現的指標含義,那麼本書的第三節中,咱們會經過一些實際的生產遇到的案例問題藉助於 Node.js 性能平臺 提供的上述工具分析過程,來幫助你們更好的理解這部分信息,也但願你們在讀完這些內容後能有所收穫,能對 Node.js 應用在生產中的使用更有信心。
本文爲雲棲社區原創內容,未經容許不得轉載。