node故障定位頂級(jí)技巧動(dòng)態(tài)追蹤Dynamic?Trace詳解
背景和行文目的
在做 node
或者其他語(yǔ)言的軟件開發(fā)時(shí),是否有以下經(jīng)歷:
- 測(cè)試環(huán)境一切正常,發(fā)到生產(chǎn)環(huán)境后,出現(xiàn)詭異問(wèn)題且難以定位
- 不同機(jī)器、不同容器上,某些邏輯呈現(xiàn)不同的結(jié)果,如時(shí)區(qū)、
host
- 對(duì)于時(shí)好時(shí)壞的玄學(xué)問(wèn)題,束手無(wú)策,沒(méi)有完整的解決思路,無(wú)頭蒼蠅般的各種嘗試,效率低下
- 遇到坑,習(xí)慣搜網(wǎng)友解決方案,然后試很多方案,都不能解決問(wèn)題,此時(shí)就會(huì)感覺頭皮發(fā)麻??
我相信,上述我說(shuō)的經(jīng)歷,大多數(shù)人都會(huì)有所共鳴。本文,我將盡可能的把我所學(xué)的動(dòng)態(tài)追蹤技術(shù)分享給大家。
文章內(nèi)容如下所示:
- 介紹
Dynamic Trace
的概念、優(yōu)勢(shì)、原理和用法(我們需要掌握的那部分) - 通過(guò)
demo
, 展示Dynamic Trace
技術(shù)的強(qiáng)大 - 實(shí)戰(zhàn)演示: 搭建
node
性能監(jiān)控easy-monitor
和構(gòu)建node
應(yīng)用,構(gòu)造詭異故障,并闡述如何用Dynamic Trace
去精確快速定位 - 分享代碼: 會(huì)把實(shí)戰(zhàn)演示的代碼放到
github
上,大家可以clone
自行去體驗(yàn) - 動(dòng)態(tài)追蹤技術(shù)的未來(lái): 介紹下目前最領(lǐng)先的
Dynamic Trace
方案
總結(jié): 做一個(gè)精簡(jiǎn)的總結(jié)
附: 其他內(nèi)容
話不多說(shuō),直接開整。
動(dòng)態(tài)追蹤技術(shù)
是什么
這里我把章亦春大佬的原話引用過(guò)來(lái):
動(dòng)態(tài)追蹤技術(shù)其實(shí)是一種后現(xiàn)代的高級(jí)調(diào)試技術(shù)。它可以幫助軟件工程師以非常低的成本,在非常短的時(shí)間內(nèi),回答一些很難的關(guān)于軟件系統(tǒng)方面的問(wèn)題,從而更快速地排查和解決問(wèn)題。
優(yōu)勢(shì)
優(yōu)勢(shì)如下:
- 隨時(shí)隨地,按需采集
- 基于操作系統(tǒng)內(nèi)核實(shí)現(xiàn),性能損耗極小
原理
動(dòng)態(tài)追蹤的事件源根據(jù)事件類型不同,主要分為靜態(tài)探針、動(dòng)態(tài)探針以及硬件事件,其原理如下圖所示:
看不懂也正常,我也看不懂,不過(guò)會(huì)用進(jìn)行,就跟你不會(huì)造車,但你可以把車開的很溜。
簡(jiǎn)單點(diǎn)說(shuō),就是在 linux
中,進(jìn)程不能直接訪問(wèn)硬件設(shè)備,當(dāng)進(jìn)程需要訪問(wèn)硬件設(shè)備時(shí),如 IO
操作,必須由用戶態(tài)切換至內(nèi)核態(tài),然后通過(guò)操作系統(tǒng)調(diào)用硬件設(shè)備。 所以可以通過(guò)跟蹤進(jìn)程產(chǎn)生的系統(tǒng)調(diào)用,來(lái)獲得參數(shù)、返回值、執(zhí)行時(shí)間,從而完成動(dòng)態(tài)追蹤。
我們用的多的是動(dòng)態(tài)探針,它可以讓我們實(shí)時(shí)分析線上運(yùn)行的程序。
用法
動(dòng)態(tài)追蹤工具有 strace
、 dtruss
、 systemtap
、 perf
、 dtrace
、 eBPF
等,我這邊使用的是 strace
和 dtruss
。
也就是 linux
環(huán)境使用 strace
, macos
系統(tǒng)使用 dtruss
。
strace 的主要參數(shù)說(shuō)明如下表所示:
參數(shù)名 | 含義 |
---|---|
-v | 輸出所有的系統(tǒng)調(diào)用,一些調(diào)用關(guān)于環(huán)境變量、狀態(tài)、輸入輸出等調(diào)用由于使用頻繁,默認(rèn)不輸出 |
-s SIZE | 指定輸出的字符串的最大長(zhǎng)度,默認(rèn)為32,文件名一直全部輸出 |
-p PID | 跟蹤指定的進(jìn)程pid |
-c | 統(tǒng)計(jì)每一系統(tǒng)調(diào)用的所執(zhí)行的時(shí)間,次數(shù)和出錯(cuò)的次數(shù)等 |
-d | 輸出strace關(guān)于標(biāo)準(zhǔn)錯(cuò)誤的調(diào)試信息 |
-f | 跟蹤由fork調(diào)用所產(chǎn)生的子進(jìn)程 |
-o filename | 將strace的輸出寫入文件filename |
-e trace=network | 跟蹤與網(wǎng)絡(luò)有關(guān)的所有系統(tǒng)調(diào)用 |
-e trace=file | 只跟蹤有關(guān)文件操作的系統(tǒng)調(diào)用 |
以上參數(shù)用的最多的就是 -v 、 -s 、 -p 、 -c 。
比如我現(xiàn)在想看部署在 linux
上的 node
應(yīng)用運(yùn)行時(shí)的信息,那我就可以執(zhí)行以下命令
strace -p 8000 -v -s 2048
參照參數(shù)說(shuō)明表解讀上述命令: 跟蹤 pid
為 8000
的進(jìn)程( node
應(yīng)用),輸出所有的運(yùn)行時(shí)系統(tǒng)調(diào)用,同時(shí)指定字符串最大長(zhǎng)度為 2048
。
同理, dtruss
的主要參數(shù)說(shuō)明如下表所示:
參數(shù)名 | 含義 |
---|---|
-a | 輸出所有詳細(xì)信息 |
-p PID | 跟蹤指定的進(jìn)程pid |
-c | 輸出系統(tǒng)調(diào)用計(jì)數(shù) |
-d | 輸出相對(duì)時(shí)間 |
-e | 輸出運(yùn)行時(shí)間 |
-f | 跟蹤由fork調(diào)用所產(chǎn)生的子進(jìn)程 |
-t | 僅檢查此系統(tǒng)調(diào)用 |
-s | 輸出堆?;厮?/td> |
-o | 輸出cpu時(shí)間 |
以上參數(shù)用的最多的就是 -p 、 -c 、 e 、 f 。
注意: 在 macos
系統(tǒng)上使用 dtruss
, 要先執(zhí)行 csrutil disable
命令行例子就不舉了,和 strace
是一樣的道理。
demo 演示
場(chǎng)景: 大家在做開發(fā)的時(shí)候,有的會(huì)遇到時(shí)區(qū)問(wèn)題,比如說(shuō)在我的機(jī)器上,時(shí)間是對(duì)的,但是在其他機(jī)器或者容器上,時(shí)間是不對(duì)的。然后你就很疑惑,開始搜,找到了所謂的執(zhí)行下某個(gè)命令就好了。
思考: 你有沒(méi)有想過(guò)兩臺(tái)機(jī)器的時(shí)間為什么不一樣,是哪個(gè)環(huán)節(jié)出了問(wèn)題,比如下面代碼在兩臺(tái)機(jī)器上的輸出結(jié)果為什么不同。
console.log(new Date().toLocaleString())
如果你知道動(dòng)態(tài)追蹤技術(shù),那你就可以開啟 **透視眼 ** , 看看在系統(tǒng)內(nèi)核層面,上面代碼究竟做了什么事情。
動(dòng)態(tài)追蹤:
參照 dtruss
參數(shù)表,我們執(zhí)行如下命令
sudo dtruss -of node time.js
控制臺(tái)如下圖:
輸出了非常多的東西,沒(méi)關(guān)系,我們直接搜 toLocaleString
關(guān)鍵詞,結(jié)果如下:
會(huì)發(fā)現(xiàn)圖中綠色框中 read(0x14, "console.log(new Date().toLocaleString())\0", 0x28)
出現(xiàn)了搜索的關(guān)鍵詞, 這句表達(dá)式的字面含義是: 系統(tǒng)在讀取 time.js
中的內(nèi)容。
我們順著上面語(yǔ)句往下閱讀,會(huì)發(fā)現(xiàn)如下圖所示重要語(yǔ)句:
看到上面三個(gè)綠色框內(nèi)容后,已經(jīng)基本確定, new Date().toLocaleString()
返回值, 是系統(tǒng)層面讀取 /etc/localtime
的內(nèi)容。也就是說(shuō),這個(gè)問(wèn)題的原因是不同機(jī)器的 /etc/localtime
不一樣
看到這你會(huì)明白:
- 為什么不同機(jī)器的返回值會(huì)不一樣
- 為什么網(wǎng)上給的時(shí)區(qū)解決方案是和
/etc/localtime
有關(guān)的
上面這個(gè)小 demo
,可以讓我們更好的感知用 Dynamic Trace
所帶來(lái)的巨大好處,所有的代碼在內(nèi)核層面都是裸奔的,它可以幫你更好的定位稀奇古怪的問(wèn)題。
實(shí)戰(zhàn)演示
為了更方便的演示,我這邊所有操作都是在 mac
電腦進(jìn)行的。 我將做以下操作
- 搭建
easy-monitor
環(huán)境 - 啟動(dòng)一個(gè)
egg
應(yīng)用 - 制造制造問(wèn)題
- 結(jié)合
easy-monitor
用Dynamic trace
進(jìn)行有序精準(zhǔn)定位 - 實(shí)戰(zhàn)總結(jié)
搭建 easy-monitor 環(huán)境
花了 20
分鐘左右,搭建好一個(gè)本地 easy-monitor
環(huán)境,項(xiàng)目結(jié)構(gòu)如下圖所示:
在官網(wǎng)文檔的情況下做了一點(diǎn)點(diǎn)優(yōu)化,優(yōu)化如下:
- 將監(jiān)控模塊整合成
monorepo
形式,使用pnpm
管理,上workspace
- 采用
turbo
,快速拉起所有包的開發(fā)模式
搭建好,一鍵啟動(dòng)開發(fā)環(huán)境,終端如下圖所示:
web
控制臺(tái)如下圖所示:
啟動(dòng)一個(gè) egg 應(yīng)用
這個(gè)按照官網(wǎng)文檔創(chuàng)建一個(gè) egg
應(yīng)用, 然后按照下圖所示進(jìn)行配置即可
過(guò)程非常簡(jiǎn)單,在上一個(gè)過(guò)程,我已經(jīng)用 turbo
一起啟動(dòng) egg
應(yīng)用了。
制造問(wèn)題
簡(jiǎn)單化, 我們寫一個(gè) node
服務(wù), 加一個(gè) bug
代碼。代碼如下:
const Controller = require('egg').Controller; const fs = require('fs'); async function read() { const data = await fs.readFileSync(__dirname + '/home.js', 'utf-8'); if (data) read(); } class CpuController extends Controller { async index() { const { ctx } = this; ctx.body = 'cpu will be 90%'; read(); } } module.exports = CpuController;
訪問(wèn)路由 /cpu
的時(shí)候, node
服務(wù)進(jìn)行讀取 home.js
文件死循環(huán)狀態(tài)。 此時(shí) 觀察 easy-monitor
cpu
狀態(tài)。如下圖所示:
會(huì)發(fā)現(xiàn), cpu
在逐漸升高,然后開始報(bào)警,遇到這種問(wèn)題,如何快速精準(zhǔn)定位呢?
Dynamic trace 精準(zhǔn)定位
在 easy-monitor
上發(fā)現(xiàn) cpu
持續(xù)升高,居高不下時(shí)。心里有一個(gè)基調(diào),那就是線上可能出問(wèn)題了,此時(shí),先冷靜,然后進(jìn)行問(wèn)題定位,步驟如下
- 確定使
cpu
居高不下的進(jìn)程,這個(gè)通過(guò)easy-monitor
或者top
命令都可以。 - 拿到進(jìn)程
pid
后,通過(guò)下面命令,來(lái)動(dòng)態(tài)追蹤,該進(jìn)程運(yùn)行時(shí)信息和熱點(diǎn)函數(shù)。
sudo dtruss -c -p 26514
執(zhí)行 10
秒后,退出,查看輸出內(nèi)容,如下圖所示:
會(huì)發(fā)現(xiàn),當(dāng)前服務(wù),其 open
、 read
和 fstat64
調(diào)用次數(shù)非常多,心里大概確定該問(wèn)題和文件操作有關(guān),然后再細(xì)致的看下輸出內(nèi)容,如下圖所示:
會(huì)發(fā)現(xiàn),存在大量的讀取路徑 /Users/godkun/monitor/apps/egg-app/app/controller/home.js
的操作,分析到這,基本可以確定問(wèn)題的原因之一了,那么后面的事情就非常簡(jiǎn)單。
快速查閱該部分代碼,發(fā)現(xiàn)存在隱藏的循序讀取邏輯,遂進(jìn)行優(yōu)化,優(yōu)化后, easy-monitor
的性能監(jiān)控顯示, cpu
立刻下降,且運(yùn)行平穩(wěn)。
實(shí)戰(zhàn)總結(jié)
我們構(gòu)造了一個(gè)循環(huán)讀取文件導(dǎo)致 node
服務(wù) cpu
居高不下的線上問(wèn)題,然后結(jié)合 easy-monitor
做性能監(jiān)控, 同時(shí)使用動(dòng)態(tài)追蹤技術(shù)進(jìn)行外科手術(shù)式精準(zhǔn)定位線上問(wèn)題,最后快速給出解決方案,整個(gè)過(guò)程有序高效。
可以這么說(shuō), 動(dòng)態(tài)追蹤 + easy-monitor
基本可以解決 node
服務(wù)遇到的所有疑難雜癥。
分享代碼
實(shí)戰(zhàn)代碼鏈接: github.com/godkun/dyna…
代碼倉(cāng)庫(kù)主要是將 easy-monitor
和 dynamic-trace
結(jié)合,在本地模擬各種問(wèn)題場(chǎng)景,來(lái)學(xué)習(xí)、理解和掌握。
寫的比較急,目前還沒(méi)做 mysql
、 redis
的 docker
化,后面有空會(huì)更新上這個(gè)功能。
動(dòng)態(tài)追蹤技術(shù)的未來(lái)
傳統(tǒng)工具
目前的動(dòng)態(tài)追蹤工具,如 dtruss
strace
perf
等,都是誕生時(shí)間很久的工具了,在用的過(guò)程中,會(huì)發(fā)現(xiàn)有大量的無(wú)用輸出,會(huì)影響定位效率,這算是一個(gè)小小的缺點(diǎn)。
潛力工具
在動(dòng)態(tài)追蹤工具中,有一個(gè)叫 eBPF
, 它可以只輸出某一行代碼活某一段代碼和系統(tǒng)交互的信息,可以讓我們迅速定位哪里出了問(wèn)題,而不是面對(duì)一大堆輸出信息,去閱讀、查找、判斷和定位。
未來(lái) + 頂尖
目前動(dòng)態(tài)追蹤領(lǐng)域,最厲害的工具應(yīng)該是章亦春大佬開發(fā)的 xray
,但目前沒(méi)有開源,未來(lái)期待春哥開源 xray
,將動(dòng)態(tài)追蹤能力拉滿,造福廣大程序員們。
總結(jié)
到此,本文即將完成,我們可以把動(dòng)態(tài)追蹤技術(shù)想象成核磁共振。當(dāng)你的應(yīng)用出現(xiàn)問(wèn)題的時(shí)候,無(wú)需停機(jī)、即刻對(duì)其做一個(gè)掃描,它可以讓你看到應(yīng)用代碼在內(nèi)核層面實(shí)時(shí)裸奔的畫面。
動(dòng)態(tài)追蹤是 一種不限于 node
服務(wù),可以定位所有服務(wù)線上故障的頂級(jí)技術(shù)。
這里補(bǔ)一個(gè)內(nèi)容,就是可能會(huì)有讀者問(wèn), easy-monitor
和 Dynamic Trace
有什么區(qū)別,為什么不能都用 easy-monitor
?
原因很簡(jiǎn)單: easy-monitor
只能到 JavaScript
的級(jí)別,而 Dynamic Trace
是直接到最底層,也就是操作系統(tǒng)的層面。所以,我們既需要 easy-monitor
,也更需要 Dynamic Trace
。
以上就是node故障定位頂級(jí)技巧動(dòng)態(tài)追蹤Dynamic Trace詳解的詳細(xì)內(nèi)容,更多關(guān)于node Dynamic Trace故障定位的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
node.js中的favicon.ico請(qǐng)求問(wèn)題處理
本文記錄了在項(xiàng)目中使用node.js請(qǐng)求favican.ico的時(shí)候會(huì)出現(xiàn)2條請(qǐng)求,浪費(fèi)資源,經(jīng)過(guò)一番改進(jìn),記錄下來(lái)過(guò)程,以后注意。2014-12-12NodeJS 將文件夾按照存放路徑變成一個(gè)對(duì)應(yīng)的JSON的方法
這篇文章主要介紹了NodeJS 將文件夾按照存放路徑變成一個(gè)對(duì)應(yīng)的JSON的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10Node.js使用bcrypt-pbkdf實(shí)現(xiàn)密碼加密
在這個(gè)數(shù)字時(shí)代,保護(hù)用戶密碼的重要性不言而喻,作為一名資深的前端開發(fā)工程師和技術(shù)博客作者,今天我將帶你詳細(xì)了解如何在 Node.js 環(huán)境中利用 bcrypt-pbkdf 模塊進(jìn)行密碼的哈希處理,確保你的應(yīng)用安全性得到有效提升,需要的朋友可以參考下2024-05-05Nodejs中解決cluster模塊的多進(jìn)程如何共享數(shù)據(jù)問(wèn)題
本篇文章主要介紹了Nodejs中解決cluster模塊的多進(jìn)程如何共享數(shù)據(jù)問(wèn)題,有需要的可以了解一下。2016-11-11nodejs開發(fā)微信小程序?qū)崿F(xiàn)密碼加密
本文給大家分享的是在使用nodejs開發(fā)微信小程序的過(guò)程中,實(shí)現(xiàn)密碼加密的示例代碼,非常簡(jiǎn)單,有需要的小伙伴可以參考下2017-07-07