panic 捕獲和 throw 崩潰
一、go 語言 panic 報錯捕獲
使用 go 語言的同學在真實專案中應該經常出現空指標使用等 panic 報錯,這類報錯與 C++ 中的 try-catch 模組不同,go 語言會一直將當前 panic 一直從報錯棧傳至最外層的棧,所以很多 go 語言的架構都會在架構中 handler 的入口新增一串程式碼
1 defer func() { 2 if x := recover(); x != nil { 3 // TODO fix panic 4 } 5 }()
這裡講幾個關鍵字
defer:註冊一個回撥函式,在當前棧退出時,按註冊入棧的順序,從最後註冊的 defer 函式開始執行,函式內部發生 panic,屬於可修復型崩潰,所以 go 語言會有序的退出棧,並執行 defer 函式
recover:捕捉 panic 異常,並打斷當前的 panic,進行處理修復,保證不會讓單個 handler 影響到整個程式
上述的異常捕獲方法想必熟悉 go 語言的同學基本都能瞭解。但下面我們瞭解一些 go 語言中無法崩潰和修復的 throw 崩潰
二、go 語言 throw 奔潰
其實 go 語言原始碼中一些地方有一些 throw 呼叫,這個函式會列印相應的 fatal msg,並退出整個程式,因為這類報錯被 go 語言認為無法動態修復的崩潰。所以這類奔潰與 panic 不同,屬於無法通過 defer 和 recover 捕獲的崩潰(因為無法修復),簡單舉兩個栗子
lock:
1 var lock sync.Mutex 2 lock.Unlock() // fatal: sync: unlock of unlocked mutex 3 4 5 // from go 1.91 6 new := atomic.AddInt32(&m.state, -mutexLocked) 7 if (new+mutexLocked)&mutexLocked == 0 { 8 throw("sync: unlock of unlocked mutex") // post a throw 9 }
map:熟悉 go 語言的開發者都知道,在 go 多攜程架構使用便利的情況下,往往存在很多執行緒不安全的變數,map 就是其中最經典的栗子,當在併發下,在沒有新增讀寫鎖的情況下對 map 進行寫、讀寫操作時,也會丟擲 throw 崩潰
testmap := make([int],1) for i := 0; i < 1000; i++ { go func() { for true{ testmap[1] = 10 // fatal: sync: curcurent map writes } }() }
需要注意的是,這類崩潰是直接 down 掉整個程序的,所以我們線上使用 go 語言進行應用開發時,一定要記得使用 supervisor 之類的程序管理工具,確保進行崩潰後先拉起來,再進行修復。否則會產生大面積機器完全宕機的情況。
再需要注意的一點是,go 語言中一個程序往往有很多 goroutinue 在同時進行,如果發生 throw 奔潰時,整個程序都會被關掉,如果通過日誌,會發現列印了無數堆疊資訊的日誌(所有 goroutinue 的日誌),這時候千萬不要在堆疊日誌上下功夫了,因為打印出來的都是正常日誌,只需要檢視日誌中的 fatal 關鍵字即可找出真正的問題所在。
轉自:https://www.cnblogs.com/novice-dxx/p/11938681.html