Recover 是一個Go語言的內建函數,可以讓進入宕機流程中的 goroutine 恢復過來,**recover 僅在延遲函數 defer 中有效**,在正常的執行過程中,調用 recover 會返回 nil 并且沒有其他任何效果,如果當前的 goroutine 陷入恐慌,調用 recover 可以捕獲到 panic 的輸入值,并且恢復正常的執行。
通常來說,不應該對進入 panic 宕機的程序做任何處理,但有時,需要我們可以從宕機中恢復,至少我們可以在程序崩潰前,做一些操作,舉個例子,當 web 服務器遇到不可預料的嚴重問題時,在崩潰前應該將所有的連接關閉,如果不做任何處理,會使得客戶端一直處于等待狀態,如果 web 服務器還在開發階段,服務器甚至可以將異常信息反饋到客戶端,幫助調試。
#### 提示
在其他語言里,宕機往往以異常的形式存在,底層拋出異常,上層邏輯通過 try/catch 機制捕獲異常,沒有被捕獲的嚴重異常會導致宕機,捕獲的異常可以被忽略,讓代碼繼續運行。
Go語言沒有異常系統,其使用 panic 觸發宕機類似于其他語言的拋出異常,recover 的宕機恢復機制就對應其他語言中的 try/catch 機制。
## 讓程序在崩潰時繼續執行
下面的代碼實現了 ProtectRun() 函數,該函數傳入一個匿名函數或閉包后的執行函數,當傳入函數以任何形式發生 panic 崩潰后,可以將崩潰發生的錯誤打印出來,同時允許后面的代碼繼續運行,不會造成整個進程的崩潰。
保護運行函數:
~~~
package main
import (
"fmt"
"runtime"
)
// 崩潰時需要傳遞的上下文信息
type panicContext struct {
function string // 所在函數
}
// 保護方式允許一個函數
func ProtectRun(entry func()) {
// 延遲處理的函數
defer func() {
// 發生宕機時,獲取panic傳遞的上下文并打印
err := recover()
switch err.(type) {
case runtime.Error: // 運行時錯誤
fmt.Println("runtime error:", err)
default: // 非運行時錯誤
fmt.Println("error:", err)
}
}()
entry()
}
func main() {
fmt.Println("運行前")
// 允許一段手動觸發的錯誤
ProtectRun(func() {
fmt.Println("手動宕機前")
// 使用panic傳遞上下文
panic(&panicContext{
"手動觸發panic",
})
fmt.Println("手動宕機后")
})
// 故意造成空指針訪問錯誤
ProtectRun(func() {
fmt.Println("賦值宕機前")
var a *int
*a = 1
fmt.Println("賦值宕機后")
})
fmt.Println("運行后")
}
~~~
## panic 和 recover 的關系
panic 和 recover 的組合有如下特性:
* 有 panic 沒 recover,程序宕機。
* 有 panic 也有 recover,程序不會宕機,執行完對應的 defer 后,從宕機點退出當前函數后繼續執行。
#### 提示
雖然 panic/recover 能模擬其他語言的異常機制,但并不建議在編寫普通函數時也經常性使用這種特性。
在 panic 觸發的 defer 函數內,可以繼續調用 panic,進一步將錯誤外拋,直到程序整體崩潰。
如果想在捕獲錯誤時設置當前函數的返回值,可以對返回值使用命名返回值方式直接進行設置。
- 1.Go語言前景
- 2.Go語言環境搭建
- 3.Go語言的基本語法
- 3.1變量
- 3.1.1變量聲明
- 3.1.2變量初始化
- 3.1.3多個變量同時賦值
- 3.1.4匿名變量
- 3.1.5變量的作用域
- 3.1.6整型
- 3.1.7浮點類型
- 3.1.8復數
- 3.1.9bool類型
- 3.1.10字符串
- 3.1.11字符類型
- 3.1.12類型轉換
- 3.2常量
- 3.1.1const關鍵字
- 3.2.2模擬枚舉
- 4.Go語言的流程控制
- 4.2循環結構
- 4.3鍵值循環
- 4.4switch語句
- 4.5goto語句
- 4.6break語句
- 4.7continue語句
- 5.Go語言的函數
- 5.1函數聲明
- 5.2函數變量
- 5.3函數類型實現接口
- 5.4閉包
- 5.5可變參數
- 5.6defer(延遲執行語句)
- 5.7處理運行時錯誤
- 5.8宕機(panic)
- 5.9宕機恢復(recover)
- 5.10Test功能測試函數
- 6.Go語言的內置容器
- 6.1數組
- 6.2切片
- 6.3map
- 6.4sync.Map
- 6.5list
- 6.6range
- 7.Go語言的結構體
- 8.Go語言的接口
- 9.Go語言的常用內置包
- 10.Go語言的并發
- 11.Go語言的文件I/O操作
- 12.Go語言的網絡編程
- 13.Go語言的反射
- 14.Go語言的數據庫編程
- 15.Go語言密碼學算法
- 16.Go語言的gin框架
- 17.Go語言的網絡爬蟲
- 18.Go語言的編譯和工具鏈