背景
上周五快下班的時(shí)候蝇刀,突然客戶報(bào)服務(wù)出現(xiàn)了503 http錯(cuò)誤譬淳,既服務(wù)不可用募舟。而且這個(gè)錯(cuò)誤是用戶在進(jìn)行某個(gè)操作的時(shí)候出現(xiàn)的祠斧。當(dāng)時(shí)去看了客戶那邊的日志,沒(méi)有發(fā)現(xiàn)有價(jià)值的信息拱礁。好在我們知道是哪個(gè)操作導(dǎo)致了這個(gè)問(wèn)題琢锋,將客戶數(shù)據(jù)拿到,然后在本地進(jìn)行測(cè)試觅彰,發(fā)現(xiàn)果然出現(xiàn)了panic,導(dǎo)致服務(wù)重啟了钮热。
經(jīng)過(guò)排查填抬,發(fā)現(xiàn)是因?yàn)榇a中出現(xiàn)了越界訪問(wèn)。但是有個(gè)問(wèn)題一直困擾隧期,為什么代碼中寫(xiě)了gin panic recover代碼飒责,但是這段代碼并沒(méi)有生效呢?帶著這個(gè)疑問(wèn)仆潮,做了響應(yīng)的實(shí)驗(yàn)宏蛉,發(fā)現(xiàn)對(duì)于go中panic理解還是沒(méi)有到位。
總結(jié)下來(lái)性置,go中panic的recover有以下兩個(gè)原則
原則1:如果程序中出現(xiàn)了panic拾并,而沒(méi)有進(jìn)行recover,那么程序會(huì)直接退出
原則2:一個(gè)協(xié)程中出現(xiàn)的panic,在另一個(gè)協(xié)程中是無(wú)法recover的嗅义,只能在本協(xié)程中進(jìn)行recover
原則3:go中某些panic是無(wú)法進(jìn)行recover的屏歹,比如并發(fā)讀寫(xiě)map(非線程安全的map結(jié)構(gòu))
下面分別對(duì)于這三個(gè)原則,編寫(xiě)相關(guān)的代碼進(jìn)行驗(yàn)證
原則1:如果程序中出現(xiàn)了panic之碗,而沒(méi)有進(jìn)行recover蝙眶,那么程序會(huì)直接退出
package main
import "fmt"
func main() {
panic("error")
fmt.Println("hello world")
}
輸出結(jié)果如下:
panic: error
goroutine 1 [running]:
main.main()
.../go/src/awesomeProject/demo1/main.go:6 +0x2c
exit status 2
可以看到hello world并沒(méi)有打印出來(lái),程序直接退出了
原則2:一個(gè)協(xié)程中出現(xiàn)的panic褪那,在另一個(gè)協(xié)程中是無(wú)法recover的幽纷,只能在本協(xié)程中進(jìn)行recover
例子1:main函數(shù)中使用recover
package main
import "fmt"
func main() {
defer func() {
fmt.Println("enter defer")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
nums := []int{1, 2, 3}
var index = 4
fmt.Println(nums[index])
fmt.Println("done")
}
上面這段程序輸出結(jié)果如下:
enter defer
runtime error: index out of range [4] with length 3
可以看到在main函數(shù)中使用recover并沒(méi)有拯救程序,程序仍然是退出的博敬。
例子2:
但是下面這段程序就可以
package main
import (
"fmt"
)
func main() {
f()
fmt.Println("hello world")
}
func f() {
defer func() {
fmt.Println("enter defer")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
nums := []int{1, 2, 3}
var index = 4
fmt.Println(nums[index])
}
輸出
enter defer
runtime error: index out of range [4] with length 3
hello world
例子3:在另一個(gè)協(xié)程中recover
package main
import (
"fmt"
"time"
)
func main() {
go func() {
if e := recover(); e != nil {
fmt.Println(e)
}
}()
go func() {
nums := []int{1, 2, 3}
var index = 4
fmt.Println(nums[index])
}()
time.Sleep(1 * time.Second)
fmt.Println("done")
}
輸出結(jié)果
panic: runtime error: index out of range [4] with length 3
goroutine 19 [running]:
main.main.func2()
..../go/src/awesomeProject/demo3/main.go:17 +0x24
created by main.main
.../go/src/awesomeProject/demo3/main.go:14 +0x30
exit status 2