偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Go Flight Recorder 終于來了,線上問題可以“回放”了!

開發(fā) 前端
簡單來說,F(xiàn)light Recorder(飛行記錄器)就像是給你的程序裝了個(gè)行車記錄儀,出了事故可以回放錄像。比起傳統(tǒng)的 trace 方式,既節(jié)省資源,又能精準(zhǔn)定位問題。這個(gè)特性在 Go1.25 正式可用了,配合之前幾個(gè)版本對 tracing 的優(yōu)化(Go1.21 降低了開銷,Go1.22 改進(jìn)了 trace 格式),整個(gè)診斷工具鏈越來越成熟了。

不知道大家在生產(chǎn)環(huán)境排查問題的時(shí)候,有沒有遇到過這樣的窘境:服務(wù)突然慢了,等你反應(yīng)過來想抓個(gè) trace 看看,問題已經(jīng)過去了。就像開車遇到異響,等你停下來檢查,聲音又沒了。

今天給大家分享 Go1.25 的一個(gè)重磅特性:Flight Recorder(飛行記錄器)。這玩意兒真的是救命神器,能讓你在問題發(fā)生后,回溯幾秒鐘前的執(zhí)行狀態(tài)。

圖片圖片

背景

先說說為什么需要這個(gè)東西。

Go 的 execution trace 功能其實(shí)一直都有,通過runtime/trace包就能收集程序執(zhí)行時(shí)的各種事件。

這對于調(diào)試延遲問題特別有用,能清楚地看到 goroutine 什么時(shí)候在執(zhí)行,更重要的是,什么時(shí)候沒在執(zhí)行。

但問題來了。

對于短期運(yùn)行的程序,比如測試、基準(zhǔn)測試或者命令行工具,你可以從頭到尾收集完整的 trace。但對于長期運(yùn)行的 Web 服務(wù),這就不現(xiàn)實(shí)了。服務(wù)器可能要運(yùn)行好幾天甚至幾周,你總不能一直開著 trace 收集數(shù)據(jù)吧?那數(shù)據(jù)量得多恐怖。

更尷尬的是,往往是某個(gè)請求超時(shí)了,或者健康檢查失敗了,等你意識到問題,想調(diào)用trace.Start()的時(shí)候,早就晚了。

有人說,那我隨機(jī)采樣不就行了?這個(gè)思路是對的,但需要一大堆基礎(chǔ)設(shè)施支撐。你得存儲、分類、處理海量的 trace 數(shù)據(jù),而且大部分?jǐn)?shù)據(jù)其實(shí)都沒啥用。更關(guān)鍵的是,當(dāng)你想排查某個(gè)具體問題的時(shí)候,這種方式基本幫不上忙。

Flight Recorder 是什么

這就是 Flight Recorder 要解決的問題。

核心思路很簡單:程序通常能感知到出問題了,但根因可能早就發(fā)生了。Flight Recorder 讓你能收集問題發(fā)生前幾秒鐘的 trace 數(shù)據(jù)。

它的工作原理是這樣的:正常收集 trace 數(shù)據(jù),但不是寫到文件或 socket 里,而是在內(nèi)存里緩存最近幾秒的數(shù)據(jù)。

一旦程序檢測到問題,隨時(shí)可以把緩沖區(qū)的內(nèi)容快照下來,精準(zhǔn)定位到問題窗口。

實(shí)戰(zhàn)案例

我們用一個(gè)實(shí)際例子來看看怎么用。

假設(shè)有這么一個(gè) HTTP 服務(wù),實(shí)現(xiàn)了一個(gè)"猜數(shù)字"的游戲。它暴露了一個(gè)/guess-number端點(diǎn),接收一個(gè)整數(shù),告訴調(diào)用者猜得對不對。

同時(shí)還有個(gè) goroutine 每分鐘發(fā)送一次統(tǒng)計(jì)報(bào)告。

核心代碼大概是這樣:

type bucket struct {
    mu      sync.Mutex
    guesses int
}

func main() {
    buckets := make([]bucket, 100)

    // 每分鐘發(fā)送報(bào)告
    gofunc() {
        forrange time.Tick(1 * time.Minute) {
            sendReport(buckets)
        }
    }()

    answer := rand.Intn(len(buckets))

    http.HandleFunc("/guess-number", func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        guess, err := strconv.Atoi(r.URL.Query().Get("guess"))
        if err != nil || !(0 <= guess && guess < len(buckets)) {
            http.Error(w, "invalid 'guess' value", http.StatusBadRequest)
            return
        }

        b := &buckets[guess]
        b.mu.Lock()
        b.guesses++
        b.mu.Unlock()

        fmt.Fprintf(w, "guess: %d, correct: %t", guess, guess == answer)

        log.Printf("HTTP request: endpoint=/guess-number guess=%d duratinotallow=%s",
            guess, time.Since(start))
    })

    log.Fatal(http.ListenAndServe(":8090", nil))
}

發(fā)送報(bào)告的函數(shù)是這樣寫的:

func sendReport(buckets []bucket) {
    counts := make([]int, len(buckets))

    for index := range buckets {
        b := &buckets[index]
        b.mu.Lock()
        defer b.mu.Unlock()

        counts[index] = b.guesses
    }

    b, err := json.Marshal(counts)
    if err != nil {
        log.Printf("failed to marshal report data: error=%s", err)
        return
    }

    url := "http://localhost:8091/guess-number-report"
    if _, err := http.Post(url, "application/json", bytes.NewReader(b)); err != nil {
        log.Printf("failed to send report: %s", err)
    }
}

上線后,用戶開始反饋有些請求特別慢。

看日志發(fā)現(xiàn),大部分請求都是微秒級的,但偶爾會有超過 100 毫秒的:

2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=69 duratinotallow=625ns
2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=42 duratinotallow=1.417μs
2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=86 duratinotallow=115.186167ms
2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=0 duratinotallow=127.993375ms

問題來了,能看出哪里有 bug 嗎?

用 Flight Recorder 排查

先別急著看答案,我們用 Flight Recorder 來排查。

首先,在 main 函數(shù)里配置并啟動 recorder:

// 配置Flight Recorder
fr := trace.NewFlightRecorder(trace.FlightRecorderConfig{
    MinAge:   200 * time.Millisecond,
    MaxBytes: 1 << 20, // 1 MiB
})
fr.Start()

這里 MinAge 設(shè)置為 200 毫秒,大概是問題窗口的 2 倍。

MaxBytes 限制緩沖區(qū)大小,避免內(nèi)存爆炸。一般來說,每秒會產(chǎn)生幾 MB 的 trace 數(shù)據(jù),繁忙的服務(wù)可能達(dá)到 10MB/s。

接下來寫個(gè)輔助函數(shù)來捕獲快照:

var once sync.Once

func captureSnapshot(fr *trace.FlightRecorder) {
    once.Do(func() {
        f, err := os.Create("snapshot.trace")
        if err != nil {
            log.Printf("opening snapshot file %s failed: %s", f.Name(), err)
            return
        }
        defer f.Close()

        _, err = fr.WriteTo(f)
        if err != nil {
            log.Printf("writing snapshot to file %s failed: %s", f.Name(), err)
            return
        }

        fr.Stop()
        log.Printf("captured a flight recorder snapshot to %s", f.Name())
    })
}

然后在請求處理函數(shù)里,當(dāng)響應(yīng)時(shí)間超過 100 毫秒時(shí)觸發(fā)快照:

if fr.Enabled() && time.Since(start) > 100*time.Millisecond {
    go captureSnapshot(fr)
}

重新運(yùn)行服務(wù),等到觸發(fā)慢請求,我們就能拿到快照文件了。

分析 trace

拿到 trace 文件后,用 Go 自帶的工具分析:

go tool trace snapshot.trace

這個(gè)工具會啟動一個(gè)本地 Web 服務(wù)器,然后在瀏覽器里打開。點(diǎn)擊"View trace by proc"可以看到時(shí)間線視圖。

在這個(gè)視圖里,我們能看到 goroutine 的執(zhí)行情況。重點(diǎn)關(guān)注右側(cè)那個(gè)巨大的空白期——大概 100 毫秒,啥都沒干!

圖片圖片

放大這個(gè)區(qū)域后,可以看到很多 goroutine 都在等待一個(gè)特定的 goroutine。點(diǎn)擊這個(gè) goroutine,查看它的棧信息,發(fā)現(xiàn)它在執(zhí)行sendReport函數(shù)。

圖片圖片

再仔細(xì)看那些"Outgoing flow"事件,它們都指向了sendReport里的Unlock操作。

圖片圖片

問題找到了!

看這段代碼:

for index := range buckets {
    b := &buckets[index]
    b.mu.Lock()
    defer b.mu.Unlock()

    counts[index] = b.guesses
}

我們本想給每個(gè) bucket 加鎖,拷貝完值就解鎖。但defer的執(zhí)行時(shí)機(jī)是函數(shù)返回時(shí),不是循環(huán)結(jié)束時(shí)。

所以這些鎖一直被持有,直到整個(gè) HTTP 請求完成后才釋放。

這就是典型的 defer 誤用場景。正確的寫法應(yīng)該是:

for index := range buckets {
    b := &buckets[index]
    b.mu.Lock()
    counts[index] = b.guesses
    b.mu.Unlock()
}

總結(jié)

Flight Recorder 真的是個(gè)好東西。它讓我們能在問題發(fā)生后,回過頭去看發(fā)生了什么,而不需要一直開著 trace 收集海量數(shù)據(jù)。

簡單來說,它就像是給你的程序裝了個(gè)行車記錄儀,出了事故可以回放錄像。比起傳統(tǒng)的 trace 方式,既節(jié)省資源,又能精準(zhǔn)定位問題。

這個(gè)特性在 Go1.25 正式可用了,配合之前幾個(gè)版本對 tracing 的優(yōu)化(Go1.21 降低了開銷,Go1.22 改進(jìn)了 trace 格式),整個(gè)診斷工具鏈越來越成熟了。

如果你經(jīng)常需要排查生產(chǎn)環(huán)境的性能問題,強(qiáng)烈建議試試這個(gè)新特性。

責(zé)任編輯:武曉燕 來源: 腦子進(jìn)煎魚了
相關(guān)推薦

2023-11-02 08:43:08

protocgo兼容

2021-12-13 20:09:33

GoElasticsearJava

2021-07-29 20:29:36

Linux c 代碼Java

2024-07-16 16:53:09

2021-04-16 15:02:38

Python 開發(fā)編程語言

2021-04-19 11:45:31

Pythonswitch編程語言

2025-06-06 08:13:47

2021-08-10 09:02:37

NumPy視圖內(nèi)存

2017-04-17 09:01:39

科技新聞早報(bào)

2009-10-22 08:50:33

Windows 7上市新聞

2013-07-12 09:59:58

Android 5.0

2023-05-29 08:38:56

popover控制懸浮層

2025-10-31 09:01:37

2022-11-08 08:29:43

Goslog 庫工具

2024-08-15 11:37:05

2021-04-20 08:03:26

單播協(xié)議TCP

2024-03-12 09:10:21

GoarenaAPI

2021-01-24 08:20:55

微信微信8.0.1移動應(yīng)用

2023-02-13 09:31:07

CSS前端

2021-10-22 15:45:32

開發(fā)技能React
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號