一,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 奔潰 post
其實 go 語言源碼中一些地方有一些 throw 調用,這個函數會打印相應的 fatal msg,並退出整個程序,由於這類報錯被 go 語言認爲沒法動態修復的崩潰。因此這類奔潰與 panic 不一樣,屬於沒法經過 defer 和 recover 捕獲的崩潰(由於沒法修復),簡單舉兩個栗子atom
lock:當使用一個初始化的鎖,並未加鎖就在代碼中就解鎖,就會發生 throw 崩潰spa
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 關鍵字便可找出真正的問題所在。