node故障定位頂級技巧動態(tài)追蹤Dynamic?Trace詳解
背景和行文目的
在做 node 或者其他語言的軟件開發(fā)時,是否有以下經(jīng)歷:
- 測試環(huán)境一切正常,發(fā)到生產(chǎn)環(huán)境后,出現(xiàn)詭異問題且難以定位
- 不同機器、不同容器上,某些邏輯呈現(xiàn)不同的結(jié)果,如時區(qū)、
host - 對于時好時壞的玄學問題,束手無策,沒有完整的解決思路,無頭蒼蠅般的各種嘗試,效率低下
- 遇到坑,習慣搜網(wǎng)友解決方案,然后試很多方案,都不能解決問題,此時就會感覺頭皮發(fā)麻??
我相信,上述我說的經(jīng)歷,大多數(shù)人都會有所共鳴。本文,我將盡可能的把我所學的動態(tài)追蹤技術(shù)分享給大家。
文章內(nèi)容如下所示:
- 介紹
Dynamic Trace的概念、優(yōu)勢、原理和用法(我們需要掌握的那部分) - 通過
demo, 展示Dynamic Trace技術(shù)的強大 - 實戰(zhàn)演示: 搭建
node性能監(jiān)控easy-monitor和構(gòu)建node應用,構(gòu)造詭異故障,并闡述如何用Dynamic Trace去精確快速定位 - 分享代碼: 會把實戰(zhàn)演示的代碼放到
github上,大家可以clone自行去體驗 - 動態(tài)追蹤技術(shù)的未來: 介紹下目前最領先的
Dynamic Trace方案
總結(jié): 做一個精簡的總結(jié)
附: 其他內(nèi)容
話不多說,直接開整。
動態(tài)追蹤技術(shù)
是什么
這里我把章亦春大佬的原話引用過來:
動態(tài)追蹤技術(shù)其實是一種后現(xiàn)代的高級調(diào)試技術(shù)。它可以幫助軟件工程師以非常低的成本,在非常短的時間內(nèi),回答一些很難的關(guān)于軟件系統(tǒng)方面的問題,從而更快速地排查和解決問題。
優(yōu)勢
優(yōu)勢如下:
- 隨時隨地,按需采集
- 基于操作系統(tǒng)內(nèi)核實現(xiàn),性能損耗極小
原理
動態(tài)追蹤的事件源根據(jù)事件類型不同,主要分為靜態(tài)探針、動態(tài)探針以及硬件事件,其原理如下圖所示:

看不懂也正常,我也看不懂,不過會用進行,就跟你不會造車,但你可以把車開的很溜。
簡單點說,就是在 linux 中,進程不能直接訪問硬件設備,當進程需要訪問硬件設備時,如 IO 操作,必須由用戶態(tài)切換至內(nèi)核態(tài),然后通過操作系統(tǒng)調(diào)用硬件設備。 所以可以通過跟蹤進程產(chǎn)生的系統(tǒng)調(diào)用,來獲得參數(shù)、返回值、執(zhí)行時間,從而完成動態(tài)追蹤。
我們用的多的是動態(tài)探針,它可以讓我們實時分析線上運行的程序。
用法
動態(tài)追蹤工具有 strace 、 dtruss 、 systemtap 、 perf 、 dtrace 、 eBPF 等,我這邊使用的是 strace 和 dtruss 。
也就是 linux 環(huán)境使用 strace , macos 系統(tǒng)使用 dtruss 。
strace 的主要參數(shù)說明如下表所示:
| 參數(shù)名 | 含義 |
|---|---|
| -v | 輸出所有的系統(tǒng)調(diào)用,一些調(diào)用關(guān)于環(huán)境變量、狀態(tài)、輸入輸出等調(diào)用由于使用頻繁,默認不輸出 |
| -s SIZE | 指定輸出的字符串的最大長度,默認為32,文件名一直全部輸出 |
| -p PID | 跟蹤指定的進程pid |
| -c | 統(tǒng)計每一系統(tǒng)調(diào)用的所執(zhí)行的時間,次數(shù)和出錯的次數(shù)等 |
| -d | 輸出strace關(guān)于標準錯誤的調(diào)試信息 |
| -f | 跟蹤由fork調(diào)用所產(chǎn)生的子進程 |
| -o filename | 將strace的輸出寫入文件filename |
| -e trace=network | 跟蹤與網(wǎng)絡有關(guān)的所有系統(tǒng)調(diào)用 |
| -e trace=file | 只跟蹤有關(guān)文件操作的系統(tǒng)調(diào)用 |
以上參數(shù)用的最多的就是 -v 、 -s 、 -p 、 -c 。
比如我現(xiàn)在想看部署在 linux 上的 node 應用運行時的信息,那我就可以執(zhí)行以下命令
strace -p 8000 -v -s 2048
參照參數(shù)說明表解讀上述命令: 跟蹤 pid 為 8000 的進程( node 應用),輸出所有的運行時系統(tǒng)調(diào)用,同時指定字符串最大長度為 2048 。
同理, dtruss 的主要參數(shù)說明如下表所示:
| 參數(shù)名 | 含義 |
|---|---|
| -a | 輸出所有詳細信息 |
| -p PID | 跟蹤指定的進程pid |
| -c | 輸出系統(tǒng)調(diào)用計數(shù) |
| -d | 輸出相對時間 |
| -e | 輸出運行時間 |
| -f | 跟蹤由fork調(diào)用所產(chǎn)生的子進程 |
| -t | 僅檢查此系統(tǒng)調(diào)用 |
| -s | 輸出堆棧回溯 |
| -o | 輸出cpu時間 |
以上參數(shù)用的最多的就是 -p 、 -c 、 e 、 f 。
注意: 在 macos 系統(tǒng)上使用 dtruss , 要先執(zhí)行 csrutil disable
命令行例子就不舉了,和 strace 是一樣的道理。
demo 演示
場景: 大家在做開發(fā)的時候,有的會遇到時區(qū)問題,比如說在我的機器上,時間是對的,但是在其他機器或者容器上,時間是不對的。然后你就很疑惑,開始搜,找到了所謂的執(zhí)行下某個命令就好了。
思考: 你有沒有想過兩臺機器的時間為什么不一樣,是哪個環(huán)節(jié)出了問題,比如下面代碼在兩臺機器上的輸出結(jié)果為什么不同。
console.log(new Date().toLocaleString())
如果你知道動態(tài)追蹤技術(shù),那你就可以開啟 **透視眼 ** , 看看在系統(tǒng)內(nèi)核層面,上面代碼究竟做了什么事情。
動態(tài)追蹤:
參照 dtruss 參數(shù)表,我們執(zhí)行如下命令
sudo dtruss -of node time.js
控制臺如下圖:

輸出了非常多的東西,沒關(guān)系,我們直接搜 toLocaleString 關(guān)鍵詞,結(jié)果如下:

會發(fā)現(xiàn)圖中綠色框中 read(0x14, "console.log(new Date().toLocaleString())\0", 0x28) 出現(xiàn)了搜索的關(guān)鍵詞, 這句表達式的字面含義是: 系統(tǒng)在讀取 time.js 中的內(nèi)容。
我們順著上面語句往下閱讀,會發(fā)現(xiàn)如下圖所示重要語句:

看到上面三個綠色框內(nèi)容后,已經(jīng)基本確定, new Date().toLocaleString() 返回值, 是系統(tǒng)層面讀取 /etc/localtime 的內(nèi)容。也就是說,這個問題的原因是不同機器的 /etc/localtime 不一樣
看到這你會明白:
- 為什么不同機器的返回值會不一樣
- 為什么網(wǎng)上給的時區(qū)解決方案是和
/etc/localtime有關(guān)的
上面這個小 demo ,可以讓我們更好的感知用 Dynamic Trace 所帶來的巨大好處,所有的代碼在內(nèi)核層面都是裸奔的,它可以幫你更好的定位稀奇古怪的問題。
實戰(zhàn)演示
為了更方便的演示,我這邊所有操作都是在 mac 電腦進行的。 我將做以下操作
- 搭建
easy-monitor環(huán)境 - 啟動一個
egg應用 - 制造制造問題
- 結(jié)合
easy-monitor用Dynamic trace進行有序精準定位 - 實戰(zhàn)總結(jié)
搭建 easy-monitor 環(huán)境
花了 20 分鐘左右,搭建好一個本地 easy-monitor 環(huán)境,項目結(jié)構(gòu)如下圖所示:

在官網(wǎng)文檔的情況下做了一點點優(yōu)化,優(yōu)化如下:
- 將監(jiān)控模塊整合成
monorepo形式,使用pnpm管理,上workspace - 采用
turbo,快速拉起所有包的開發(fā)模式
搭建好,一鍵啟動開發(fā)環(huán)境,終端如下圖所示:

web 控制臺如下圖所示:

啟動一個 egg 應用
這個按照官網(wǎng)文檔創(chuàng)建一個 egg 應用, 然后按照下圖所示進行配置即可

過程非常簡單,在上一個過程,我已經(jīng)用 turbo 一起啟動 egg 應用了。
制造問題
簡單化, 我們寫一個 node 服務, 加一個 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;
訪問路由 /cpu 的時候, node 服務進行讀取 home.js 文件死循環(huán)狀態(tài)。 此時 觀察 easy-monitor cpu 狀態(tài)。如下圖所示:

會發(fā)現(xiàn), cpu 在逐漸升高,然后開始報警,遇到這種問題,如何快速精準定位呢?
Dynamic trace 精準定位
在 easy-monitor 上發(fā)現(xiàn) cpu 持續(xù)升高,居高不下時。心里有一個基調(diào),那就是線上可能出問題了,此時,先冷靜,然后進行問題定位,步驟如下
- 確定使
cpu居高不下的進程,這個通過easy-monitor或者top命令都可以。 - 拿到進程
pid后,通過下面命令,來動態(tài)追蹤,該進程運行時信息和熱點函數(shù)。
sudo dtruss -c -p 26514
執(zhí)行 10 秒后,退出,查看輸出內(nèi)容,如下圖所示:

會發(fā)現(xiàn),當前服務,其 open 、 read 和 fstat64 調(diào)用次數(shù)非常多,心里大概確定該問題和文件操作有關(guān),然后再細致的看下輸出內(nèi)容,如下圖所示:

會發(fā)現(xiàn),存在大量的讀取路徑 /Users/godkun/monitor/apps/egg-app/app/controller/home.js 的操作,分析到這,基本可以確定問題的原因之一了,那么后面的事情就非常簡單。
快速查閱該部分代碼,發(fā)現(xiàn)存在隱藏的循序讀取邏輯,遂進行優(yōu)化,優(yōu)化后, easy-monitor 的性能監(jiān)控顯示, cpu 立刻下降,且運行平穩(wěn)。
實戰(zhàn)總結(jié)
我們構(gòu)造了一個循環(huán)讀取文件導致 node 服務 cpu 居高不下的線上問題,然后結(jié)合 easy-monitor 做性能監(jiān)控, 同時使用動態(tài)追蹤技術(shù)進行外科手術(shù)式精準定位線上問題,最后快速給出解決方案,整個過程有序高效。
可以這么說, 動態(tài)追蹤 + easy-monitor 基本可以解決 node 服務遇到的所有疑難雜癥。
分享代碼
實戰(zhàn)代碼鏈接: github.com/godkun/dyna…
代碼倉庫主要是將 easy-monitor 和 dynamic-trace 結(jié)合,在本地模擬各種問題場景,來學習、理解和掌握。
寫的比較急,目前還沒做 mysql 、 redis 的 docker 化,后面有空會更新上這個功能。
動態(tài)追蹤技術(shù)的未來
傳統(tǒng)工具
目前的動態(tài)追蹤工具,如 dtruss strace perf 等,都是誕生時間很久的工具了,在用的過程中,會發(fā)現(xiàn)有大量的無用輸出,會影響定位效率,這算是一個小小的缺點。
潛力工具
在動態(tài)追蹤工具中,有一個叫 eBPF , 它可以只輸出某一行代碼活某一段代碼和系統(tǒng)交互的信息,可以讓我們迅速定位哪里出了問題,而不是面對一大堆輸出信息,去閱讀、查找、判斷和定位。
未來 + 頂尖
目前動態(tài)追蹤領域,最厲害的工具應該是章亦春大佬開發(fā)的 xray ,但目前沒有開源,未來期待春哥開源 xray ,將動態(tài)追蹤能力拉滿,造福廣大程序員們。
總結(jié)
到此,本文即將完成,我們可以把動態(tài)追蹤技術(shù)想象成核磁共振。當你的應用出現(xiàn)問題的時候,無需停機、即刻對其做一個掃描,它可以讓你看到應用代碼在內(nèi)核層面實時裸奔的畫面。
動態(tài)追蹤是 一種不限于 node 服務,可以定位所有服務線上故障的頂級技術(shù)。
這里補一個內(nèi)容,就是可能會有讀者問, easy-monitor 和 Dynamic Trace 有什么區(qū)別,為什么不能都用 easy-monitor ?
原因很簡單: easy-monitor 只能到 JavaScript 的級別,而 Dynamic Trace 是直接到最底層,也就是操作系統(tǒng)的層面。所以,我們既需要 easy-monitor ,也更需要 Dynamic Trace 。
以上就是node故障定位頂級技巧動態(tài)追蹤Dynamic Trace詳解的詳細內(nèi)容,更多關(guān)于node Dynamic Trace故障定位的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
NodeJS 將文件夾按照存放路徑變成一個對應的JSON的方法
這篇文章主要介紹了NodeJS 將文件夾按照存放路徑變成一個對應的JSON的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10
Node.js使用bcrypt-pbkdf實現(xiàn)密碼加密
在這個數(shù)字時代,保護用戶密碼的重要性不言而喻,作為一名資深的前端開發(fā)工程師和技術(shù)博客作者,今天我將帶你詳細了解如何在 Node.js 環(huán)境中利用 bcrypt-pbkdf 模塊進行密碼的哈希處理,確保你的應用安全性得到有效提升,需要的朋友可以參考下2024-05-05
Nodejs中解決cluster模塊的多進程如何共享數(shù)據(jù)問題
本篇文章主要介紹了Nodejs中解決cluster模塊的多進程如何共享數(shù)據(jù)問題,有需要的可以了解一下。2016-11-11
nodejs開發(fā)微信小程序?qū)崿F(xiàn)密碼加密
本文給大家分享的是在使用nodejs開發(fā)微信小程序的過程中,實現(xiàn)密碼加密的示例代碼,非常簡單,有需要的小伙伴可以參考下2017-07-07

