運作時異常
panic
在通常情況下,函數向其調用方報告錯誤的方式都是傳回一個
error
類型的值。但是當遇到緻命錯誤的時候,很可能會使程式無法繼續運作。Go推薦通過調用
panic
函數來報告緻命錯誤,它會停止目前控制流程,并引發一個運作時恐慌。例如:
package main
import "errors"
func main() {
outerFunc()
}
func outerFunc() {
innerFunc()
}
func innerFunc() {
panic(errors.New("an intended fatal error"))
}
程式執行後的結果為:
D:\gotest>go run main.go
panic: an intended fatal error
goroutine 1 [running]:
main.innerFunc(...)
D:/gotest/main.go:14
main.outerFunc(...)
D:/gotest/main.go:10
main.main()
D:/gotest/main.go:6 +0x77
exit status 2
當調用
innerFunc
函數中的
panic
函數後,
innerFunc
的執行會被停止。緊接着,流程控制權會交給調用方
outerFunc
函數。然後,
outerFunc
函數的執行也會停止。運作時恐慌就沿着調用棧反方向進行傳播,直至到達目前
goroutine
的調用棧的最頂層。一旦達到頂層,就意味着該
goroutine
調用棧中所有函數的執行都已經被停止了,程式已經崩潰。
當然,運作時恐慌并不都是通過調用
panic
函數的方式引發,也可以由Go的運作時系統來引發。例如發生像數組下标越界或類型斷言失敗這樣的運作錯誤時,Go運作時會觸發運作時
panic
。
recover
為了避免恐慌造成的程式崩潰。Go提供了專用于"攔截"運作時恐慌的内建函數
recover
,它可以使目前的程式從恐慌狀态中恢複并重新獲得流程控制權。
recover
函數被調用後,會傳回一個
interface{}
類型的結果。如果當時的程式正處于運作時恐慌的狀态,那麼這個結果就會是
非nil
的。使用方法:
defer func() {
if p := recover(); p != nil {
fmt.Printf("Recovered panic: %s\n", p)
}
}()
beego架構的處理
beego
架構有一些初始化的函數,當你引用
beego
架構時,也就同時執行了初始化函數。(有關包的初始化,可以點選Go init函數詳解進行檢視)。其中
beego\config.go
檔案中的
init
函數涉及到了程式對
panic
的處理。
func init () {
BConfig = newBconfig()
// ...
}
func newBConfig() *Config {
return &Config{
AppName: "beego",
RunMode: DEV,
RouterCaseSensitive: true,
ServerName: "beegoServer:" + VERSION,
RecoverPanic: true,
RecoverFunc: recoverPanic,
//...
}
}
func recoverPanic(ctx *context.Context) {
if err := recover(); err != nil {
if err == ErrAbort {
return
}
if !BConfig.RecoverPanic {
panic(err)
}
if BConfig.EnableErrorsShow {
if _,ok := ErrorMaps[fmt.Sprint(err)]; ok {
exception(fmt.Sprint(err),ctx)
return
}
}
var stack string
logs.Critical("the request url is ", ctx.Input.URL())
logs.Critical(""Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
logs.Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
if BConfig.RunMode == DEV || BConfig.EnableErrorsRender {
showErr(err, ctx, stack)
}
}
}
從上面的代碼我們可以看到,使用
beego
架構啟動程式時,會初始化一個
recoverPanic
方法。當程式遇到異常時,會進行異常恢複,防止程式崩潰。
beego
在
runmode
模式為
dev
,且
EnableErrorsRender
(是否将錯誤資訊進行渲染,預設值為
true
,即出錯會提示友好的出錯頁面,對于
API
類型的應用,可能需要将該選項設定為
false
,以防止在
dev
模式下不必要的模闆渲染資訊傳回)為
true
的情況下,會在前端将異常展示出來。而對于其他模式,
beego
隻會在控制台将
panic
出現的調用棧資訊依次列印出來。
參考文章
- Go并發程式設計實戰