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