golang 程式的異常退出
同事反饋了一個問題,一個微服務異常退出。查了許久沒有發現一個是一個協程異常導致的整個程序退出了。程式的異常情況其實基本上是可控的,找到異常原因,修復問題上線是可以的。但是這裡體現了兩個知識點:1、golang的一個協程異常,如果沒有捕獲,回導致整個程序退出。
這裡就不舉例子說明了,自己可以寫個很簡單的demo,通過go func() {}裡面使用panic產生恐慌試驗下。
2、關於defer、panic、recover的使用理解
golang不支援java語言中的try...catch...finally這種異常,因為...此處忽略了一千字:smile:
defer的原意是推遲、延期。它的思想類似與C++的解構函式,不過go語言中的析構的不是物件,而是函式,defer就是用來新增函式結束時執行的語句。注意這裡強調的是新增,而不是指定。因為不同於C++中的解構函式是靜態的,Go中的defer是動態的。
func Defer() (result int) {
defer func() {
result ++
}()
return 0
}
上述函式輸出值為1,因為在函式返回前執行了defer函式,修改了result值。在上面的defer函式前增加一個返回,如下:
func Defer() (resultint) {
return 0
defer func() {
result ++
}()
return 0
}
上述函式返回0,因為還沒有註冊執行defer,函式已經返回了。
此外,在一個函式中,defer可以新增多個,這樣就形成了一個defer棧,既然說到棧,當然遵循先進後出的特性。例子如下:
func Defer() () {
defer func() {
fmt.Println("result = 1")
}()
defer func() {
fmt.Println("result = 2")
}()
defer func() {
fmt.Println("result = 3")
}()
}
上述例子輸出

panic:原意為恐慌、驚慌。上面已經提到過,在程式中使用panic會讓程式立馬退出(除非recover)。所以,go語言的異常,是真的異常了。函式在執行panic時,函式不會再往下走了,執行時並不是立刻向上傳遞panic,而是到defer,等defer函式執行完,panic會繼續向上傳遞,所以這時候defer有點類似try-catch-finally中的finally。
recover:原意重新獲得、恢復。上面說到panic函式並不會立刻返回,而是先去執行defer,然後再返回。這時候如果在defer中阻止panic繼續向上傳遞,那就異常的處理機制完善了。
go語言提供了recover內建函式,前面提到,在defer中可以使用recover來捕獲panic,禁止panic向上傳遞,從而不會導致整個程式的退出。
那麼問題來了,假如編寫的程式存在N個協程,是在每個協程中的defer中recover還是可以在主協程中的defer中recover?下面通過例子來說明下:
例1:
func Recover() {
go func() {
defer func() {
if r := recover();r != nil {
fmt.Println("Recoverd in f", r)
}
}()
time.Sleep(time.Second *2)
panic(1)
}()
for ;; {
fmt.Println("sleeping...")
time.Sleep(time.Second *4)
}
}
上面的例子輸出

在defer中捕獲到panic為1的恐慌。是否可以在主協程中捕獲到這個恐慌那?
例2:
func Recover() {
defer func() {
if r := recover();r != nil {
fmt.Println("Recoverd in f", r)
}
}()
go func() {
time.Sleep(time.Second *2)
panic(1)
}()
for ;; {
fmt.Println("sleeping...")
time.Sleep(time.Second *4)
}
}

程式會終止,並不會捕捉到上面的異常。