溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Golang Recover中有哪些坑需要注意

發(fā)布時間:2020-12-23 09:36:31 來源:億速云 閱讀:447 作者:小新 欄目:編程語言

這篇文章主要介紹了Golang Recover中有哪些坑需要注意,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

1.error

Golang被詬病非常多的一點(diǎn)就是缺少強(qiáng)大方便的異常處理機(jī)制,大部分高級編程語言,比如Java、PHP、Python等都擁有一種try catch機(jī)制,這種異常捕獲機(jī)制可以非常方便的處理程序運(yùn)行中可能出現(xiàn)的各種意外情況。

嚴(yán)格來說,在Go里面,錯誤和異常是2種不同的類型,錯誤一般是指程序產(chǎn)生的邏輯錯誤,或者意料之中的意外情況,而且異常一般就是panic,比如角標(biāo)越界、段錯誤。

對于錯誤,Golang采用了一種非常原始的手段,我們必須手動處理可能產(chǎn)生的每一個錯誤,一般會把錯誤返回給調(diào)用方,下面這種寫法在Go里面十分常見:

package mainimport (
    "errors"
    "fmt")func main() {
    s, err := say()
    if err != nil {
        fmt.Printf("%s\n", err.Error())
    } else {
        fmt.Printf("%s\n", s)
    }}func say() (string, error) {
    // do something
    return "", errors.New("something error")}復(fù)制代碼

這種寫法最大的問題就是每一個error都需要判斷處理,非常繁瑣,如果使用try catch機(jī)制,我們就可以統(tǒng)一針對多個函數(shù)調(diào)用可能產(chǎn)生的錯誤做處理,節(jié)省一點(diǎn)代碼和時間。不過咱們今天不是來討論Go的異常錯誤處理機(jī)制的,這里只是簡單說一下。

2.panic

一般錯誤都是顯示的,程序明確返回的,而異常往往是隱示的,不可預(yù)測的,比如下面的代碼:

package mainimport "fmt"func main() {
    fmt.Printf("%d\n", cal(1,2))
    fmt.Printf("%d\n", cal(5,2))
    fmt.Printf("%d\n", cal(5,0)) //panic: runtime error: integer pide by zero 
    fmt.Printf("%d\n", cal(9,5))}func cal(a, b int) int {
    return a / b}復(fù)制代碼

在執(zhí)行第三個計算的時候會發(fā)生一個panic,這種錯誤會導(dǎo)致程序退出,下面的代碼的就無法執(zhí)行了。當(dāng)然你可以說這種錯誤理論上是可以預(yù)測的,我們只要在cal函數(shù)內(nèi)部做好處理就行了。

然而實際開發(fā)中,會發(fā)生panic的地方可能特別多,而且不是這種一眼就能看出來的,在Web服務(wù)中,這樣的panic會導(dǎo)致整個Web服務(wù)掛掉,特別危險。

3.recover

雖然沒有try catch機(jī)制,Go其實有一種類似的recover機(jī)制,功能弱了點(diǎn),用法很簡單:

package mainimport "fmt"func main() {
    fmt.Printf("%d\n", cal(1, 2))
    fmt.Printf("%d\n", cal(5, 2))
    fmt.Printf("%d\n", cal(5, 0))
    fmt.Printf("%d\n", cal(9, 2))}func cal(a, b int) int {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    return a / b}復(fù)制代碼

首先,大家得理解defer的作用,簡單說defer就類似于面向?qū)ο罄锩娴奈鰳?gòu)函數(shù),在這個函數(shù)終止的時候會執(zhí)行,即使是panic導(dǎo)致的終止。

所以,在cal函數(shù)里面每次終止的時候都會檢查有沒有異常產(chǎn)生,如果產(chǎn)生了我們可以處理,比如說記錄日志,這樣程序還可以繼續(xù)執(zhí)行下去。

4.注意的坑

一般defer recover這種機(jī)制經(jīng)常用在常駐進(jìn)程的應(yīng)用,比如Web服務(wù),在Go里面,每一個Web請求都會分配一個goroutine去處理,在沒有做任何處理的情況下,假如某一個請求發(fā)生了panic,就會導(dǎo)致整個服務(wù)掛掉,這是不可接受的,所以在Web應(yīng)用里面必須使用recover保證即使某一個請求發(fā)生錯誤也不影響其它請求。

這里我使用一小段代碼模擬一下:

package mainimport (
    "fmt")func main() {
    requests := []int{12, 2, 3, 41, 5, 6, 1, 12, 3, 4, 2, 31}
    for n := range requests {
        go run(n) //開啟多個協(xié)程
    }

    for {
        select {}
    }}func run(num int) {
    //模擬請求錯誤
    if num%5 == 0 {
        panic("請求出錯")
    }
    fmt.Printf("%d\n", num)}復(fù)制代碼

上面這段代碼無法完整執(zhí)行下去,因為其中某一個協(xié)程必然會發(fā)生panic,從而導(dǎo)致整個應(yīng)用掛掉,其它協(xié)程也停止執(zhí)行。

解決方法和上面一樣,我們只需要在run函數(shù)里面加入defer recover,整個程序就會非常健壯,即使發(fā)生panic,也會完整的執(zhí)行下去。

func run(num int) {
    defer func() {
        if err := recover();err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    if num%5 == 0 {
        panic("請求出錯")
    }
    fmt.Printf("%d\n", num)}復(fù)制代碼

上面的代碼只是演示,真正的坑是:如果你在run函數(shù)里面又啟動了其它協(xié)程,這個協(xié)程發(fā)生的panic是無法被recover的,還是會導(dǎo)致整個進(jìn)程掛掉,我們改造了一下上面的例子:

func run(num int) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    if num%5 == 0 {
        panic("請求出錯")
    }
    go myPrint(num)}func myPrint(num int) {
    if num%4 == 0 {
        panic("請求又出錯了")
    }
    fmt.Printf("%d\n", num)}復(fù)制代碼

我在run函數(shù)里面又通過協(xié)程的方式調(diào)用了另一個函數(shù),而這個函數(shù)也會發(fā)生panic,你會發(fā)現(xiàn)整個程序也掛了,即使run函數(shù)有recover也沒有任何作用,這意味著我們還需要在myPrint函數(shù)里面加入recover。但是如果你不使用協(xié)程的方式調(diào)用myPrint函數(shù),直接調(diào)用的話還是可以捕獲recover的。

總結(jié)一下就是defer recover這種機(jī)制只是針對當(dāng)前函數(shù)和以及直接調(diào)用的函數(shù)可能產(chǎn)生的panic,它無法處理其調(diào)用產(chǎn)生的其它協(xié)程的panic,這一點(diǎn)和try catch機(jī)制不一樣。

理論上講,所有使用協(xié)程的地方都必須做defer recover處理,這樣才能保證你的應(yīng)用萬無一失,不過開發(fā)中可以根據(jù)實際情況而定,對于一些不可能出錯的函數(shù)加了還影響性能。

Go的Web服務(wù)也是一樣,默認(rèn)的recover機(jī)制只能捕獲一層,如果你在這個請求的處理中又使用了其它協(xié)程,那么必須非常慎重,畢竟只要發(fā)生一個panic,整個Web服務(wù)就會掛掉。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享Golang Recover中有哪些坑需要注意內(nèi)容對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI