go 中更加強大的 traces
-
原文地址:More powerful Go execution traces
-
原文作者:Michael Knyszek
-
本文永久鏈接:
https://github.com/gocn/translator/blob/master/2024/w12_more_powerful_go_execution_traces.md
-
譯者:小超人
-
Go 版本: 1.22+
-
runtime/trace 包包含一個用於理解並且排查 go 程序問題的強大工具。通過這個功能我們可以跟蹤每個 goroutine 在一段時間內的執行情況。使用 go tool trace 命令 (或優秀的開源 (gotraceui 工具) ) ,可以對這些跟蹤數據進行可視化處理和探索。
trace 的神奇之處在於輕鬆地揭祕程序,這是通過其他的方式是很難辦到的。例如,如果沒有執行採樣,在 CPU 的 profile 文件中很難看到大量 goroutines 因爲同一通道而阻塞的併發瓶頸。但在執行 trace 中,阻塞執行的情況會以驚人的清晰度顯示出來,阻塞的 goroutines 堆棧 traces 會迅速指出罪魁禍首。
Go 開發者甚至可以使用 tasks, regions, 和 logs 這些工具來觀測自己的程序,從而將高層關注點與低層執行細節關聯起來。
問題
遺憾的是,執行 trace 中的大量信息是被丟棄的。從歷史上看,有四大問題阻礙了執行 trace 。
-
trace 開銷大。
-
trace 不能很好地擴展,並且可能會變得太大而無法分析。
-
沒有量化的指標確定從什麼時候開始捕獲程序中出現的一些不良的行爲。
-
由於缺乏解析和解釋執行 trace 的公共軟件包,只有最具有探索精神的 gophers 才能通過編程分析 trace。
如果你在過去幾年中有使用過 trace,那麼你很可能會因爲其中一個或多個問題而感到失望。但我們很高興地告訴大家,在過去的兩個 Go 版本中,我們在所有這四個方面都取得了很大的進步。
低成本 tracing
在 Go 1.21 之前,對於許多應用程序來說,trace 會佔用 CPU 的 10-20% 的開銷,這就限制了 trace 的使用場景,而不能像持續 CPU profileing 那樣使用。事實證明,trace 的大部分使用成本都來自於回溯。運行時產生的許多事件都附有 trace 的堆棧信息,這對於實際確定 goroutines 在執行過程中的關鍵時刻在做什麼非常有價值。
得益於 Felix Geisendörfer 和 Nick Ripley 在優化回溯效率方面所做的工作,執行 trace 的 CPU 運行時開銷已大幅降低,許多應用程序的 CPU 運行時開銷已降至 1-2%。你可以在 FelixFelix’s 的精彩博客中閱讀更多相關信息。
可擴展的 trace
trace 格式及其事件是圍繞相對高效的排放來進行設計的,但需要工具來解析和保持整個 trace 的狀態。分析一個幾百兆的 trace 可能需要幾個 G 的內存!
不幸的是,這個問題在生成 trace 的時候造成的。爲了降低運行時開銷,所有事件都被寫入線程本地的緩衝區。但這意味着事件的出現順序與真實順序不符,因此 trace 工具就有責任弄清到底發生了什麼。
要在保持較低開銷的同時擴大 trace 範圍,關鍵在於偶爾分割正在生成的 trace。每個分割點的行爲有點像一次同時禁用並重新啓用 trace。到目前爲止的所有 trace 數據將代表一個完整的、自成一體的 trace,而新的 trace 數據將無縫地從原來的位置開始。
可以想象,要解決這個問題,需要重新思考並重寫 trace 運行時的基礎工作。我們很高興地宣佈,這項工作已在 Go 1.22 中完成,並已普遍可用。重寫帶來了很多很好的改進,包括對 go tool trace
命令的一些改進。如果你想了解更多細節,請參閱設計文檔。
(備註:go tool trace
仍會將完整的 trace 加載到內存中,但對於 Go 1.22+ 程序生成的 trace 來說,取消這一限制並且現在是可用的)。
黑匣子
假設你正在處理一項網絡服務,而 RPC 耗時很長。你不能在知道 RPC 已經耗時很長時間時之後纔開始跟蹤,因爲導致請求緩慢的根本原因已經過去了,而且沒有被記錄下來。
有一種技術可以幫助解決這個問題,叫做黑匣子,你可能已經在其他編程環境中熟悉了這種技術。黑匣子的原理是持續 tracing,並始終保持最新的跟蹤數據,以防萬一。然後,一旦有有趣的事情發生,程序就可以直接寫出它所擁有的一切!
在 tracing 可以拆分之前,這幾乎是不可能實現的。但是,由於連續跟蹤的開銷較低,而且運行時可以隨時拆分跟蹤,因此現在可以直接實現黑匣子。
因此,我們很高興地宣佈將在 golang.org/x/exp/trace package 軟件包中提供實驗性的黑匣子記錄器。
請試用一下!下面是一個設置黑匣子用來記錄長 HTTP 請求的示例,供你開始使用。
fr := trace.NewFlightRecorder()
fr.Start()
var once sync.Once
http.HandleFunc("/my-endpoint", func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
doWork(w, r)
if time.Since(start) > 300*time.Millisecond {
once.Do(func() {
var b bytes.Buffer
_, err = fr.WriteTo(&b)
if err != nil {
log.Print(err)
return
}
if err := os.WriteFile("trace.out", b.Bytes(), 0o755); err != nil {
log.Print(err)
return
}
})
}
})
log.Fatal(http.ListenAndServe(":8080", nil))
如果你有任何反饋意見,無論是正面的還是負面的,proposal issue 63185 都請與我們分享!
trace reader API
在重寫 trace 實現的同時,我們還努力重構其他 trace 內部結構,如 go tool trace
。因此,我們嘗試創建一個足以共享的 trace reader API,使 trace 更方便使用。
就像黑匣子一樣,我們很高興地宣佈,我們也有一個實驗性的 trace reader 的 API,希望與大家分享。它和黑匣子在同一個軟件包中。
我們認爲在此基礎上進行開發已經足夠好了,所以請試用一下!下面是一個示例,用於檢測因爲等待網絡而阻塞的 goroutine 的比例。
r, err := trace.NewReader(os.Stdin)
if err != nil {
log.Fatal(err)
}
var blocked int
var blockedOnNetwork int
for {
ev, err := r.ReadEvent()
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
if ev.Kind() == trace.EventStateTransition {
st := ev.StateTransition()
if st.Resource.Kind == trace.ResourceGoroutine {
id := st.Resource.Goroutine()
from, to := st.GoroutineTransition()
if from.Executing(); to == trace.GoWaiting {
blocked++
if strings.Contains(st.Reason, "network") {
blockedOnNetwork++
}
}
}
}
}
p := 100 * float64(blockedOnNetwork) / float64(blocked)
fmt.Printf("%2.3f%% instances of goroutines blocking were to block on the network\n", p)
就像黑匣子一樣,有一個問題的提案 也是留下反饋意見的好地方!
Dominik Honnef 很早就試用了這個 API,並提供了很好的反饋,還爲舊版本的 trace API 提供了支持。
謝謝!
這項工作的完成,在很大程度上要歸功於診斷工作組成員 diagnostics working group 的幫助。診斷工作組在一年多之前成立,由 go 社區相關人員組成,並向公衆開放。
在此,我們要向去年定期參加診斷會議的社區成員:Felix Geisendörfer、Nick Ripley、Rhys Hiltner、Dominik Honnef、Bryan Boreham 和 thepudds 表示感謝。
你們的討論、反饋和付出對我們取得今天的成績起到了至關重要的作用。謝謝你們
引用
-
gotraceui tool
-
rethinking and rewriting a lot of the foundation of the trace implementation
-
trace tasks
-
trace regions
-
trace logs
-
felix’s great blog post
-
rethinking and rewriting a lot of the foundation of the trace implementation
-
A lot of nice improvements
-
tracer design document
-
1.22 removing this limitation
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Ujtv1j4ty-Jf_D6VJMf0Dw