新聞中心
Go語(yǔ)言的類型系統(tǒng)會(huì)在編譯時(shí)捕獲很多錯(cuò)誤,但有些錯(cuò)誤只能在運(yùn)行時(shí)檢查,如數(shù)組訪問(wèn)越界、空指針引用等,這些運(yùn)行時(shí)錯(cuò)誤會(huì)引起宕機(jī)。

宕機(jī)不是一件很好的事情,可能造成體驗(yàn)停止、服務(wù)中斷,就像沒(méi)有人希望在取錢時(shí)遇到 ATM 機(jī)藍(lán)屏一樣,但是,如果在損失發(fā)生時(shí),程序沒(méi)有因?yàn)殄礄C(jī)而停止,那么用戶將會(huì)付出更大的代價(jià),這種代價(jià)可以是金錢、時(shí)間甚至生命,因此,宕機(jī)有時(shí)也是一種合理的止損方法。
一般而言,當(dāng)宕機(jī)發(fā)生時(shí),程序會(huì)中斷運(yùn)行,并立即執(zhí)行在該 goroutine(可以先理解成線程)中被延遲的函數(shù)(defer 機(jī)制),隨后,程序崩潰并輸出日志信息,日志信息包括 panic value 和函數(shù)調(diào)用的堆棧跟蹤信息,panic value 通常是某種錯(cuò)誤信息。
對(duì)于每個(gè) goroutine,日志信息中都會(huì)有與之相對(duì)的,發(fā)生 panic 時(shí)的函數(shù)調(diào)用堆棧跟蹤信息,通常,我們不需要再次運(yùn)行程序去定位問(wèn)題,日志信息已經(jīng)提供了足夠的診斷依據(jù),因此,在我們填寫問(wèn)題報(bào)告時(shí),一般會(huì)將宕機(jī)和日志信息一并記錄。
雖然Go語(yǔ)言的 panic 機(jī)制類似于其他語(yǔ)言的異常,但 panic 的適用場(chǎng)景有一些不同,由于 panic 會(huì)引起程序的崩潰,因此 panic 一般用于嚴(yán)重錯(cuò)誤,如程序內(nèi)部的邏輯不一致。任何崩潰都表明了我們的代碼中可能存在漏洞,所以對(duì)于大部分漏洞,我們應(yīng)該使用Go語(yǔ)言提供的錯(cuò)誤機(jī)制,而不是 panic。
手動(dòng)觸發(fā)宕機(jī)
Go語(yǔ)言可以在程序中手動(dòng)觸發(fā)宕機(jī),讓程序崩潰,這樣開(kāi)發(fā)者可以及時(shí)地發(fā)現(xiàn)錯(cuò)誤,同時(shí)減少可能的損失。
Go語(yǔ)言程序在宕機(jī)時(shí),會(huì)將堆棧和 goroutine 信息輸出到控制臺(tái),所以宕機(jī)也可以方便地知曉發(fā)生錯(cuò)誤的位置,那么我們要如何觸發(fā)宕機(jī)呢,示例代碼如下所示:
package main
func main() {
panic("crash")
}代碼運(yùn)行崩潰并輸出如下:
panic: crash
goroutine 1 [running]:
main.main()
D:/code/main.go:4 +0x40
exit status 2
以上代碼中只用了一個(gè)內(nèi)建的函數(shù) panic() 就可以造成崩潰,panic() 的聲明如下:
func panic(v interface{}) //panic() 的參數(shù)可以是任意類型的。
在運(yùn)行依賴的必備資源缺失時(shí)主動(dòng)觸發(fā)宕機(jī)
regexp 是Go語(yǔ)言的正則表達(dá)式包,正則表達(dá)式需要編譯后才能使用,而且編譯必須是成功的,表示正則表達(dá)式可用。
編譯正則表達(dá)式函數(shù)有兩種,具體如下:
1) func Compile(expr string) (*Regexp, error)
編譯正則表達(dá)式,發(fā)生錯(cuò)誤時(shí)返回編譯錯(cuò)誤同時(shí)返回 Regexp 為 nil,該函數(shù)適用于在編譯錯(cuò)誤時(shí)獲得編譯錯(cuò)誤進(jìn)行處理,同時(shí)繼續(xù)后續(xù)執(zhí)行的環(huán)境。
2) func MustCompile(str string) *Regexp
當(dāng)編譯正則表達(dá)式發(fā)生錯(cuò)誤時(shí),使用 panic 觸發(fā)宕機(jī),該函數(shù)適用于直接使用正則表達(dá)式而無(wú)須處理正則表達(dá)式錯(cuò)誤的情況。
MustCompile 的代碼如下:
func MustCompile(str string) *Regexp {
regexp, error := Compile(str)
if error != nil {
panic(`regexp: Compile(` + quote(str) + `): ` + error.Error())
}
return regexp
}代碼說(shuō)明如下:
- 第 1 行,編譯正則表達(dá)式函數(shù)入口,輸入包含正則表達(dá)式的字符串,返回正則表達(dá)式對(duì)象。
- 第 2 行,Compile() 是編譯正則表達(dá)式的入口函數(shù),該函數(shù)返回編譯好的正則表達(dá)式對(duì)象和錯(cuò)誤。
- 第 3 和第 4 行判斷如果有錯(cuò),則使用 panic() 觸發(fā)宕機(jī)。
- 第 6 行,沒(méi)有錯(cuò)誤時(shí)返回正則表達(dá)式對(duì)象。
手動(dòng)宕機(jī)進(jìn)行報(bào)錯(cuò)的方式不是一種偷懶的方式,反而能迅速報(bào)錯(cuò),終止程序繼續(xù)運(yùn)行,防止更大的錯(cuò)誤產(chǎn)生,不過(guò),如果任何錯(cuò)誤都使用宕機(jī)處理,也不是一種良好的設(shè)計(jì)習(xí)慣,因此應(yīng)根據(jù)需要來(lái)決定是否使用宕機(jī)進(jìn)行報(bào)錯(cuò)。
在宕機(jī)時(shí)觸發(fā)延遲執(zhí)行語(yǔ)句
當(dāng) panic() 觸發(fā)的宕機(jī)發(fā)生時(shí),panic() 后面的代碼將不會(huì)被運(yùn)行,但是在 panic() 函數(shù)前面已經(jīng)運(yùn)行過(guò)的 defer 語(yǔ)句依然會(huì)在宕機(jī)發(fā)生時(shí)發(fā)生作用,參考下面代碼:
package main
import "fmt"
func main() {
defer fmt.Println("宕機(jī)后要做的事情1")
defer fmt.Println("宕機(jī)后要做的事情2")
panic("宕機(jī)")
}代碼輸出如下:
宕機(jī)后要做的事情2
宕機(jī)后要做的事情1
panic: 宕機(jī)
goroutine 1 [running]:
main.main()
D:/code/main.go:8 +0xf8
exit status 2
對(duì)代碼的說(shuō)明:
- 第 6 行和第 7 行使用 defer 語(yǔ)句延遲了 2 個(gè)語(yǔ)句。
- 第 8 行發(fā)生宕機(jī)。
宕機(jī)前,defer 語(yǔ)句會(huì)被優(yōu)先執(zhí)行,由于第 7 行的 defer 后執(zhí)行,因此會(huì)在宕機(jī)前,這個(gè) defer 會(huì)優(yōu)先處理,隨后才是第 6 行的 defer 對(duì)應(yīng)的語(yǔ)句,這個(gè)特性可以用來(lái)在宕機(jī)發(fā)生前進(jìn)行宕機(jī)信息處理。
名稱欄目:創(chuàng)新互聯(lián)GO教程:Go語(yǔ)言宕機(jī)(panic)——程序終止運(yùn)行
網(wǎng)頁(yè)鏈接:http://www.dlmjj.cn/article/dhscddp.html


咨詢
建站咨詢
