Go 語言的宕機恢復,如何防止程序奔潰
Recover
是一個 Go 語言的內建函數,可以讓進入宕機流程中的 goroutine
恢復過來,recover
僅在延遲函數 defer
中有效,在正常的執行過程中,調用 recover
會返回 nil
並且沒有其他任何效果,如果當前的 goroutine
陷入恐慌,調用 recover
可以捕獲到 panic
的輸入值,並且恢復正常的執行。
通常來說,不應該對進入 panic
宕機的程序做任何處理,但有時,需要我們可以從宕機中恢復,至少我們可以在程序崩潰前,做一些操作,舉個例子,當 web 服務器遇到不可預料的嚴重問題時,在崩潰前應該將所有的連接關閉,如果不做任何處理,會使得客戶端一直處於等待狀態,如果 web 服務器還在開發階段,服務器甚至可以將異常信息反饋到客戶端,幫助調試。
提示
在其他語言裏,宕機往往以異常的形式存在,底層拋出異常,上層邏輯通過 try/catch 機制捕獲異常,沒有被捕獲的嚴重異常會導致宕機,捕獲的異常可以被忽略,讓代碼繼續運行。
Go 語言沒有異常系統,其使用 panic
觸發宕機類似於其他語言的拋出異常,recover
的宕機恢復機制就對應其他語言中的 try/catch
機制。
讓程序在崩潰時繼續執行
下面的代碼實現了 ProtectRun()
函數,該函數傳入一個匿名函數或閉包後的執行函數,當傳入函數以任何形式發生 panic 崩潰後,可以將崩潰發生的錯誤打印出來,同時允許後面的代碼繼續運行,不會造成整個進程的崩潰。
保護運行函數:
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8// 崩潰時需要傳遞的上下文信息
9type panicContext struct {
10 function string // 所在函數
11}
12
13// 保護方式允許一個函數
14func ProtectRun(entry func()) {
15
16 // 延遲處理的函數
17 defer func() {
18
19 // 發生宕機時,獲取panic傳遞的上下文並打印
20 err := recover()
21
22 switch err.(type) {
23 case runtime.Error: // 運行時錯誤
24 fmt.Println("runtime error:", err)
25 default: // 非運行時錯誤
26 fmt.Println("error:", err)
27 }
28
29 }()
30
31 entry()
32
33}
34
35func main() {
36 fmt.Println("運行前")
37
38 // 允許一段手動觸發的錯誤
39 ProtectRun(func() {
40
41 fmt.Println("手動宕機前")
42
43 // 使用panic傳遞上下文
44 panic(&panicContext{
45 "手動觸發panic",
46 })
47
48 fmt.Println("手動宕機後")
49 })
50
51 // 故意造成空指針訪問錯誤
52 ProtectRun(func() {
53
54 fmt.Println("賦值宕機前")
55
56 var a *int
57 *a = 1
58
59 fmt.Println("賦值宕機後")
60 })
61
62 fmt.Println("運行後")
63}
代碼輸出結果:
運行前
手動宕機前
error: &{手動觸發 panic}
賦值宕機前
runtime error: runtime error: invalid memory address or nil pointer dereference
運行後
對代碼的說明:
第 9 行聲明描述錯誤的結構體,保存執行錯誤的函數。
第 17 行使用 defer 將閉包延遲執行,當 panic 觸發崩潰時,ProtectRun() 函數將結束運行,此時 defer 後的閉包將會發生調用。
第 20 行,recover() 獲取到 panic 傳入的參數。
第 22 行,使用 switch 對 err 變量進行類型斷言。
第 23 行,如果錯誤是有 Runtime 層拋出的運行時錯誤,如空指針訪問、除數爲 0 等情況,打印運行時錯誤。
第 25 行,其他錯誤,打印傳遞過來的錯誤數據。
第 44 行,使用 panic 手動觸發一個錯誤,並將一個結構體附帶信息傳遞過去,此時,recover 就會獲取到這個結構體信息,並打印出來。
第 57 行,模擬代碼中空指針賦值造成的錯誤,此時會由 Runtime 層拋出錯誤,被 ProtectRun() 函數的 recover() 函數捕獲到。
panic 和 recover 的關係
panic
和 recover
的組合有如下特性:
-
有
panic
沒recover
,程序宕機。 -
有
panic
也有recover
,程序不會宕機,執行完對應的defer
後,從宕機點退出當前函數後繼續執行。
提示
雖然 panic/recover
能模擬其他語言的異常機制,但並不建議在編寫普通函數時也經常性使用這種特性。
在 panic
觸發的 defer
函數內,可以繼續調用 panic
,進一步將錯誤外拋,直到程序整體崩潰。
如果想在捕獲錯誤時設置當前函數的返回值,可以對返回值使用命名返回值方式直接進行設置。
文章首發:https://www.lmlphp.com/user/10603/article/item/422458
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/0igEX_764Q2v4wFP2D_7mg