你是否想要嘗試進行 Node.js 應用開發可是又總聽人說它不安全、穩定性差,想在公司推廣擴張大前端的能力範疇和影響又說服不了技術領導。前端
JavaScript 發展到今天,早已脫離本來瀏覽器的戰場,藉助於 Node.js 的誕生將其觸角伸到了服務端、PC 跨平臺客戶端方案等各個領域,可是與此同時,JS Runtime 對於絕大部分的開發者來講又一如既往的處於黑盒狀態——開發者沒法感知其運行狀態,出現一些性能、內存問題時也沒有很好的工具鏈進行更深刻的支持。node
本書將在基於 Node.js 性能平臺 的基礎上,從多個你們開發上線過程當中可能遇到的疑難雜症的視角,觀察如何去發現、定位和解決這些問題,幫助讀者構建對 Node.js 這門語言的更多信心。ios
由於本書將屬於 Node.js 開發進階的內容,所以咱們但願本書的讀者具有如下的基本技能:git
本書首發在 Github,倉庫地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,雲棲社區會同步更新。github
常規排查的指標數據庫
當咱們第一次遇到線上異常時,不少人會感受無從下手。本節做爲預備篇,將從服務器異常時常見的排查指標開始,幫助你們創建一個更加直觀的問題處理體系。
畢竟若是咱們面對線上異常時,若是連繫統哪裏有問題都不知道,那麼後續的藉助 Node.js 性能平臺 更深刻定位問題代碼就更加無從談起了。瀏覽器
當咱們的應用出現問題時,首先須要去查看咱們應用的錯誤日誌,觀察在這段時間內是否是有錯誤在一直拋出,致使了咱們的服務不穩定。
這一塊的信息顯然是因各個應用而異的,當咱們的項目比較大(Ecs/Docker 節點比較多)的時候,就須要對錯誤日誌的進行統一的採集收集來保證出問題時的快速定位。一個比較簡單的統一日誌平臺能夠設計以下:緩存
其中的採集服務器和 Agent 上報之間通常會採用消息隊列(Kafka)來做爲緩衝區減輕雙方的負載,ELK 就是一個比較成熟的日誌服務。安全
有了統一的日誌平臺後,當咱們的應用出現問題時,首先應該去日誌平臺上查看當前的錯誤日誌信息,特別是對於那些在 頻繁出現 的錯誤日誌應當引發警戒,須要去仔細地結合產生錯誤的代碼段進行回溯確認是不是形成當前服務不穩定的元兇,Node.js 性能平臺 也實現了一個簡單的錯誤日誌回溯 + 告警的系統,本書第二部分會更詳細說明。服務器
若是在上述的錯誤日中沒有看到可疑的信息(實際上錯誤日誌以及本節的系統指標排查前後順序並沒有固定,你們能夠視本身的需求進行),那麼接下來咱們就應該關注下問題是否是由於服務器或者 Node.js 應用自己的負載到了極限致使的問題。一些比較常見的你們須要關注的系統指標以下所示:
下面逐一講解這些可能存在問題的系統指標。
使用 top 命令來觀察和 Node.js 應用進程的 CPU 和 Memory 負載狀況。通常來講,對於 CPU 很高 Node.js 進程,咱們可使用 Node.js 性能平臺 提供的 CPU Profiling 工具來在線 Dump 出當前的 Javascript 運行狀況,進而找到熱點代碼進行優化,具體在本書第二部分會有更詳細地說明。
那麼對於 Memory 負載很高的狀況,正常來講就是發生了內存泄漏(或者有預期以外的內存分配致使溢出),那麼一樣的咱們能夠用性能平臺提供的工具來在線 Dump 出當前的 Javascript 堆內存和服務化的分析來結合你的業務代碼找到產生泄漏的邏輯。
這裏須要注意的是,目前性能平臺可以進行詳盡分析的地方集中在你的 JS 代碼上,對於徹底是 C++ 擴展執行的或者徹底的 V8/Libuv 底層執行(這部分功能後面會補上)的邏輯,以及不分配在 V8 Heap 上的內存,性能平臺目前沒有更好的辦法來進行分析處理。而實際上在咱們遇到的案例中,你們編寫的 JS 代碼出問題佔了絕大部分,也就是性能平臺目前針對 JS 部分比較完善的在線 Dump + 服務化分析基本上可以解決開發者 95% 甚至以上的問題了。
使用 df 命令能夠觀察當前的磁盤佔用狀況,這個也是很是常見的問題,不少開發者會忽略對服務器磁盤的監控告警,當咱們的日誌/核心轉儲等大文件逐漸將磁盤打滿到 100% 的時候,Node.js 應用極可能會沒法正常運行,Node.js 性能平臺 目前也提供了對磁盤的監控,在本書第二部分一樣會有更詳細地說明。
使用 top/iostat 和 cat /proc/${pid}/io 來查看當前的 I/O 負載,這一項的負載很高的話,也會使得 Node.js 應用出現卡死等狀況。
絕大部分的 Node.js 應用其實是 Web 應用,每一個用戶的鏈接都會建立一個 Socket 鏈接,在一些異常狀況下(好比遭受半鏈接攻擊或者內核參數設置不合理),服務器上會有大量的 TIME_WAIT 狀態的鏈接,而大量的 TIME_WAIT 積壓會致使 Node.js 應用的卡死(內核沒法爲新的請求分配建立新的 TCP 鏈接),咱們可使用 netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}' 命令來確認這個問題。
線上 Node.js 應用故障每每也伴隨着進程的 Crash,藉助於一些守護進程的自檢重啓拉起,咱們的服務依舊在運行,可是咱們不該該去忽略這些意外的 Crash —— 當流量增大或者形成服務器的問題用戶訪問被別有用心之人抓住時,咱們集羣就變得朝不保夕了。
絕大部分狀況下,會形成 Node.js 應用 Crash 掉的錯誤日誌每每並不會記錄到咱們的錯誤日誌文件中,幸運的是,服務器內核提供了一項機制幫助咱們在應用 Crash 時自動地生成核心轉儲(Core dump)文件,讓開發者能夠在過後進行分析還原案發現場。
核心轉儲(Core dump)其實是咱們的應用意外崩潰終止時,計算機自動記錄下進程 Crash 掉那一刻的內存分配信息、Program counter 以及堆棧指針等關鍵信息來生成核心轉儲文件,所以獲取到核心轉儲文件後,咱們能夠經過 MDB、GDB、LLDB 等工具便可實現解析診斷實際進程的 Crash 緣由。
觸發核心轉儲生成轉儲文件目前主要有兩種方式:
使用 ulimit -c unlimited
打開內核限制,而且考慮到默認運行模式下,Node.js 對 JS 形成的 Crash 是不會觸發核心轉儲動做的,所以咱們能夠在 Node 應用啓動時加上參數 --abort-on-uncaught-exception
來對出現未捕獲的異常時也能讓內核觸發自動的核心轉儲動做。
手動調用 gcore <pid>
(可能須要 sudo 權限)的方式來手動生成,由於此時 Node.js 應用依舊在運行中,因此實際上這種方式通常用於 「活體檢驗」,用於 Node.js 進程假死狀態 下的問題定位。
這裏須要注意的是,以上的生成核心轉儲的操做都 並無那麼安全務必記得對服務器磁盤進行監控和告警**。
獲取到 Node.js 應用生成的核心轉儲文件後,咱們能夠藉助於 Node.js 性能平臺 提供的在線 Core dump 文件分析功能進行分析定位進程 Crash 的緣由了,具體用法會在本書第二部分進行說明。
本節從常見的幾個服務器問題點,給你們對線上 Node.js 應用出現故障時如何去排查定位有了一些大概的印象,本章也是後續內容的一個預備知識,瞭解了這部份內容,才能在後面的一些實戰案例中明白爲什麼咱們忽略了其它而選擇詳盡地服務化分析其中的一些要點。
而核心轉儲的深刻分析則可以幫助咱們解決 Node.js 應用的絕大部分底層故障,由於其能夠還原出問題 JavaScript 代碼和引起問題的參數,功能很是地強大。
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。