Node.js 应用故障排查手册 —— 大纲与常规问题指标简介
楔子
你是否想要嘗試進行 Node.js 應用開發但是又總聽人說它不安全、穩定性差,想在公司推廣擴張大前端的能力范疇和影響又說服不了技術領導。
JavaScript 發展到今天,早已脫離原本瀏覽器的戰場,借助于 Node.js 的誕生將其觸角伸到了服務端、PC 跨平臺客戶端方案等各個領域,但是與此同時,JS Runtime 對于絕大部分的開發者來說又一如既往的處于黑盒狀態——開發者無法感知其運行狀態,出現一些性能、內存問題時也沒有很好的工具鏈進行更深入的支持。
本書將在基于?Node.js 性能平臺?的基礎上,從多個大家開發上線過程中可能遇到的疑難雜癥的視角,觀察如何去發現、定位和解決這些問題,幫助讀者構建對 Node.js 這門語言的更多信心。
因為本書將屬于 Node.js 開發進階的內容,因此我們希望本書的讀者具備以下的基本技能:
- 常規的 Node.js 應用開發的能力
- 常規的服務器性能指標參數的理解,比如 CPU、Memory、Load、文件打開數等
- 常見的數據庫、緩存等操作
- 負載均衡、多進程模型
- 如果使用容器,容器的基本知識,資源管理等
本書首發在 Github,倉庫地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云棲社區會同步更新。
常規排查的指標
當我們第一次遇到線上異常時,很多人會感覺無從下手。本節作為預備篇,將從服務器異常時常見的排查指標開始,幫助大家建立一個更加直觀的問題處理體系。
畢竟如果我們面對線上異常時,如果連系統哪里有問題都不知道,那么后續的借助?Node.js 性能平臺?更深入定位問題代碼就更加無從談起了。
錯誤日志
當我們的應用出現問題時,首先需要去查看我們應用的錯誤日志,觀察在這段時間內是不是有錯誤在一直拋出,導致了我們的服務不穩定。
這一塊的信息顯然是因各個應用而異的,當我們的項目比較大(Ecs/Docker 節點比較多)的時候,就需要對錯誤日志的進行統一的采集收集來保證出問題時的快速定位。一個比較簡單的統一日志平臺可以設計如下:
其中的采集服務器和 Agent 上報之間一般會采用消息隊列(Kafka)來作為緩沖區減輕雙方的負載,ELK?就是一個比較成熟的日志服務。
有了統一的日志平臺后,當我們的應用出現問題時,首先應該去日志平臺上查看當前的錯誤日志信息,特別是對于那些在?頻繁出現?的錯誤日志應當引起警惕,需要去仔細地結合產生錯誤的代碼段進行回溯確認是否是造成當前服務不穩定的元兇,Node.js 性能平臺?也實現了一個簡單的錯誤日志回溯 + 告警的系統,本書第二部分會更詳細說明。
系統指標
如果在上述的錯誤日中沒有看到可疑的信息(實際上錯誤日志以及本節的系統指標排查先后順序并無固定,大家可以視自己的需求進行),那么接下來我們就應該關注下問題是不是因為服務器或者 Node.js 應用本身的負載到了極限導致的問題。一些比較常見的大家需要關注的系統指標如下所示:
- CPU &?Memory
- Disk 磁盤占用率
- I/O 負載
- TCP 連接狀態
下面逐一講解這些可能存在問題的系統指標。
I. CPU &?Memory
使用?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% 甚至以上的問題了。
II. Disk 磁盤占用率
使用??df?命令可以觀察當前的磁盤占用情況,這個也是非常常見的問題,很多開發者會忽略對服務器磁盤的監控告警,當我們的日志/核心轉儲等大文件逐漸將磁盤打滿到 100% 的時候,Node.js 應用很可能會無法正常運行,Node.js 性能平臺?目前也提供了對磁盤的監控,在本書第二部分同樣會有更詳細地說明。
III. I/O 負載
使用?top/iostat?和?cat /proc/${pid}/io?來查看當前的 I/O 負載,這一項的負載很高的話,也會使得 Node.js 應用出現卡死等情況。
IV. TCP 連接狀態
絕大部分的 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])}'?命令來確認這個問題。
核心轉儲(Core dump)
線上 Node.js 應用故障往往也伴隨著進程的 Crash,借助于一些守護進程的自檢重啟拉起,我們的服務依舊在運行,但是我們不應該去忽略這些意外的 Crash —— 當流量增大或者造成服務器的問題用戶訪問被別有用心之人抓住時,我們集群就變得岌岌可危了。
絕大部分情況下,會造成 Node.js 應用 Crash 掉的錯誤日志往往并不會記錄到我們的錯誤日志文件中,幸運的是,服務器內核提供了一項機制幫助我們在應用 Crash 時自動地生成核心轉儲(Core dump)文件,讓開發者可以在事后進行分析還原案發現場。
核心轉儲
核心轉儲(Core dump)實際上是我們的應用意外崩潰終止時,計算機自動記錄下進程 Crash 掉那一刻的內存分配信息、Program counter 以及堆棧指針等關鍵信息來生成核心轉儲文件,因此獲取到核心轉儲文件后,我們可以通過 MDB、GDB、LLDB 等工具即可實現解析診斷實際進程的 Crash 原因。
生成文件
觸發核心轉儲生成轉儲文件目前主要有兩種方式:
I. 設置內核參數
使用?ulimit -c unlimited?打開內核限制,并且考慮到默認運行模式下,Node.js 對 JS 造成的 Crash 是不會觸發核心轉儲動作的,因此我們可以在 Node 應用啟動時加上參數?--abort-on-uncaught-exception?來對出現未捕獲的異常時也能讓內核觸發自動的核心轉儲動作。
II. 手動調用
手動調用?gcore <pid>?(可能需要 sudo 權限)的方式來手動生成,因為此時 Node.js 應用依舊在運行中,所以實際上這種方式一般用于?「活體檢驗」,用于?Node.js 進程假死狀態?下的問題定位。
這里需要注意的是,以上的生成核心轉儲的操作都?并沒有那么安全務必記得對服務器磁盤進行監控和告警**。
獲取到 Node.js 應用生成的核心轉儲文件后,我們可以借助于?Node.js 性能平臺?提供的在線 Core dump 文件分析功能進行分析定位進程 Crash 的原因了,具體用法會在本書第二部分進行說明。
小結
本節從常見的幾個服務器問題點,給大家對線上 Node.js 應用出現故障時如何去排查定位有了一些大概的印象,本章也是后續內容的一個預備知識,了解了這部分內容,才能在后面的一些實戰案例中明白為何我們忽略了其它而選擇詳盡地服務化分析其中的一些要點。
而核心轉儲的深入分析則能夠幫助我們解決 Node.js 應用的絕大部分底層故障,因為其可以還原出問題 JavaScript 代碼和引發問題的參數,功能非常地強大。
?
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的Node.js 应用故障排查手册 —— 大纲与常规问题指标简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么强烈禁止开发人员使用isSucce
- 下一篇: 深入解读MySQL8.0 新特性 :Cr