轉載
原文地址:https://www.cnblogs.com/wt645631686/p/9562997.html
Go 錯誤處理
Go 語言通過內置的錯誤接口提供了非常簡單的錯誤處理機制。
error類型是一個接口類型,這是它的定義:
type error interface { Error() string }
函數通常在最后的返回值中返回錯誤信息。使用errors.New 可返回一個錯誤信息:
var err = errors.New("this is an error")
錯誤字符串由於相對固定,一般在包作用域聲明,應盡量減少在使用時直接使用 errors.New 返回。
1) errors 包
Go語言的 errors 中對 New 的定義非常簡單,代碼如下:
// 創建錯誤對象 func New(text string) error { return &errorString{text} } // 錯誤字符串 type errorString struct { s string } // 返回發生何種錯誤 func (e *errorString) Error() string { return e.s }
Golang錯誤處理函數defer、panic、recover
- 在默認情況下,當發生錯誤(panic)后,程序就會終止運行
- 如果發生錯誤后,可以捕獲錯誤,並通知管理人員(郵件或者短信),程序還可以繼續運行,這當然無可厚非
- errors.New("錯誤信息"),會返回一個error類型的值,表示一個錯誤
- panic內置函數,接收一個interface()類型的值(也就是任何值都可以)作為參數,可以接收error類型的變量,輸出錯誤信息,並退出程序
- GO語言追求簡潔優雅,GO語言不類似php支出try catch操作
- GO語言中引入的處理方式為:defer、panic、recover
- GO可以拋出一個panic異常。然后在defer中通過recover捕獲異常再處理
自定義錯誤的處理與運用:
package main import ( "fmt" "errors" ) //此函數讀取配置文件信息 //如果文件名不正確,返回自定義錯誤 func readConfFile(FileName string) (err error) { //返回error類型 if FileName == "config.ini" { return nil //表示沒有錯誤 } else { return errors.New("讀取文件錯誤") } } func error_func(){ err := readConfFile("config.ini1") //這里故意寫錯,報錯代碼在第一塊,如果寫對,在第二塊 if err != nil { //如果讀取文件發生錯誤,就輸出這個錯誤,並終止程序 panic(err) //這個函數作用是打印錯誤信息,並終止程序 } fmt.Println("error_func()繼續執行") } func main() { error_func() fmt.Printf("發生錯誤后面的代碼") } //【報錯信息】 //一、 // panic: 讀取文件錯誤 // goroutine 1 [running]: // main.error_func() // D:/goproject/src/main/hello.go:22 +0xdf // main.main() // D:/goproject/src/main/hello.go:27 +0x27 // exit status 2 //二、 // error_func()繼續執行 // 發生錯誤后面的代碼
recover+defer的運用:
package main import ( "fmt" "time" ) //這里舉例,在數學計算中0是不可以作為被除數的 func error_func() { //這里使用defer + recover來捕獲處理異常 defer func() { //defer就是把匿名函數壓入到defer棧中,等到執行完畢后或者發生異常后調用匿名函數(必須要先聲明defer,否則不能捕獲到panic異常) err := recover() //recover是內置函數,可以捕獲到異常 if err != nil { //說明有錯誤 fmt.Println("err=", err) //當然這里可以把錯誤的詳細位置發送給開發人員 //send email to admin } }() num1 := 10 num2 := 0 res := num1 / num2 fmt.Println("res=", res) } func main() { //這樣程序不會輕易掛掉 error_func() i := 0 for { i++ fmt.Println("發生錯誤后面的代碼", i) time.Sleep(time.Second) } }
defer語句是在return之后執行的,例如:
func test() (result int) { defer func() { result = 12 }() return 10 } func main() { fmt.Println(test()) // 12 }
在一個函數中panic被調用后,其defer語句仍會執行,例如:
package main import ( "fmt" "errors" ) func foo()(n int) { defer func() { if r := recover(); r != nil { fmt.Println("再然后④\n") n++ fmt.Println("最后⑤\n") } }() fmt.Println("首先①\n") n++ fmt.Printf("然后②n=%v\n" ,n) fmt.Println("然后③\n") panic(errors.New("i'm a bug")) fmt.Println("沒走\n") return n } func main() { n := foo() fmt.Printf("n最后的值%v", n) }
首先① 然后②n=1 然后③ 再然后④ 最后⑤ n最后的值2
ecover 是一個Go語言的內建函數,可以讓進入宕機流程中的 goroutine 恢復過來,recover 僅在延遲函數 defer 中有效,在正常的執行過程中,調用 recover 會返回 nil 並且沒有其他任何效果,如果當前的 goroutine 陷入恐慌,調用 recover 可以捕獲到 panic 的輸入值,並且恢復正常的執行。
通常來說,不應該對進入 panic 宕機的程序做任何處理,但有時,需要我們可以從宕機中恢復,至少我們可以在程序崩潰前,做一些操作,舉個例子,當 web 服務器遇到不可預料的嚴重問題時,在崩潰前應該將所有的連接關閉,如果不做任何處理,會使得客戶端一直處於等待狀態,如果 web 服務器還在開發階段,服務器甚至可以將異常信息反饋到客戶端,幫助調試。
提示
在其他語言里,宕機往往以異常的形式存在,底層拋出異常,上層邏輯通過 try/catch 機制捕獲異常,沒有被捕獲的嚴重異常會導致宕機,捕獲的異常可以被忽略,讓代碼繼續運行。
Go語言沒有異常系統,其使用 panic 觸發宕機類似於其他語言的拋出異常,recover 的宕機恢復機制就對應其他語言中的 try/catch 機制
panic 和 recover 的關系
panic 和 recover 的組合有如下特性:
- 有 panic 沒 recover,程序宕機。
- 有 panic 也有 recover,程序不會宕機,執行完對應的 defer 后,從宕機點退出當前函數后繼續執行。
提示
雖然 panic/recover 能模擬其他語言的異常機制,但並不建議在編寫普通函數時也經常性使用這種特性。
在 panic 觸發的 defer 函數內,可以繼續調用 panic,進一步將錯誤外拋,直到程序整體崩潰。
如果想在捕獲錯誤時設置當前函數的返回值,可以對返回值使用命名返回值方式直接進行設置。