国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

利用 Golang 中的 Recover 處理錯誤

開發 前端
recover? 是一個內建函數,它使我們有可能在發生 panic 時重新獲得控制。它僅在被調用的延遲函數中產生效果。在延遲函數之外調用時,它總是返回 nil?。如果我們處于 panic 模式,調用 recover? 會返回傳遞給 panic 函數的值。

Golang 中的 recover 是一個鮮為人知但非常有趣和強大的功能。讓我們看看它是如何工作的,以及在 Outreach.io 中如何利用它來處理 Kubernetes 中的錯誤。

Panic/Defer/Recover 基本上是 Golang 中對于其他編程語言中 throw/finally/catch 概念的替代品。它們有一些共同之處,但在一些重要細節上有所不同。

Defer

要充分理解 recover,我們首先需要談論 defer 語句。defer 關鍵字前置于函數調用之前,使得該調用在當前函數返回之前執行。當我們在一個函數中使用多個 defer 語句時,它們按照后進先出的順序執行,這使得創建清理邏輯變得非常容易,如下例所示:

package main

import (
    "context"
    "database/sql"
    "fmt"
)

func readRecords(ctx context.Context) error {
    db, err := sql.Open("sqlite3", "file:test.db?cache=shared&mode=memory")
    if err != nil {
        return err
    }
    defer db.Close() // 這個函數調用將在 readRecords 函數返回時第三個執行

    conn, err := db.Conn(ctx)
    if err != nil {
        return err
    }
    defer conn.Close() // 這個函數調用將在第二個執行

    rows, err := conn.QueryContext(ctx, "SELECT id FROM users")
    if err != nil {
        return err
    }
    defer rows.Close() // 這個函數調用將在第一個執行

    for rows.Next() {
        var id int64
        if err := rows.Scan(&id); err != nil {
            return err
        }
        fmt.Println("ID:", id)
    }
    return nil
}

func main() {
    readRecords(context.Background())
}

Panic

我們需要談論的第二個主題是 panic,它是一個導致當前 goroutine 進入 panic 模式的函數。當前函數中的正常執行流程被停止,僅執行 defer 語句,然后對調用者函數執行相同的操作,因此一直冒泡到堆棧的頂部(main 函數),然后使程序崩潰。panic 可以直接調用(傳遞一個值作為參數),也可以由運行時錯誤引起。例如,由于空指針解引用:

package main

import "fmt"

func main() {
    var x *string
    fmt.Println(*x)
}
// panic: runtime error: invalid memory address or nil pointer dereference

Recover

recover 是一個內建函數,它使我們有可能在發生 panic 時重新獲得控制。它僅在被調用的延遲函數中產生效果。在延遲函數之外調用時,它總是返回 nil。如果我們處于 panic 模式,調用 recover 會返回傳遞給 panic 函數的值。基本示例:

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered: %v\\n", r)
        }
    }()

    panic("spam, egg, sausage, and spam")
}
// Recovered: spam, egg, sausage, and spam

我們可以以同樣的方式從運行時錯誤中恢復:

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered: %v\\n", r)
        }
    }()

    var x *string
    fmt.Println(*x)
}
// Recovered: runtime error: invalid memory address or nil pointer dereference

在這種情況下,recover 返回的值的類型是錯誤(更準確地說是 runtime.errorString)。

有一個限制:我們不能直接從 recover 塊中返回值,因為在 recover 塊中的 return 語句僅從延遲函數中返回,而不是從周圍的函數中返回:

package main

import "fmt"

func foo() int {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered: %v\\n", r)
            return 1 // "too many return values" 因為我們僅從匿名函數返回
        }
    }()

    panic("spam, egg, sausage, and spam")
}

func main() {
    x := foo()
    fmt.Println(x)
}

如果我們想要更改函數返回的值,我們需要使用命名返回值:

package main

import "fmt"

func foo() (ret int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered: %v\\n", r)
            ret = 1
        }
    }()

    panic("spam, egg, sausage, and spam")
}

func main() {
    x := foo()
    fmt.Println("value:", x)
}
// Recovered: spam, egg, sausage, and spam
// value: 1

一個更實際的例子,將 panic 轉換為普通錯誤的轉換可能如下所示:

package main

import (
    "fmt"

    "github.com/google/uuid"
)

// processInput 嘗試將輸入字符串轉換為 uuid.UUID
// 它將 panic 轉換為錯誤
func processInput(input string) (u uuid.UUID, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v", r)
        }
    }()

    // 一些可能引發 panic 的邏輯(也可以是第三方邏輯),例如:
    u = uuid.MustParse(input)
    return u, nil
}

func main() {
    u, err := processInput("xxx")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(u)
}
// panic: uuid: Parse(xxx): invalid UUID length: 3
// 00000000-0000-0000-0000-000000000000

現在讓我們嘗試一些稍微

復雜的東西。假設我們在 Kubernetes 中運行,并且我們想要編寫一個通用的 recover 函數,處理所有未捕獲的 panic 和運行時錯誤,并收集它們的堆棧跟蹤,以便我們可以以結構化的方式記錄它們(例如,以 JSON 格式)。

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/pkg/errors"
)

func foo() string {
    var s *string
    return *s
}

func handlePanic(r interface{}) error {
    var errWithStack error
    if err, ok := r.(error); ok {
        errWithStack = errors.WithStack(err)
    } else {
        errWithStack = errors.Errorf("%+v", r)
    }
    return errWithStack
}

func main() {
    logger := log.New(os.Stdout, "", 0)

    defer func() {
        if r := recover(); r != nil {
            err := handlePanic(r)
            logger.Println(
                "panic occurred",
                "msg", err.Error(),
                "stack", fmt.Sprintf("%+v", err),
            )
        }
    }()

    fmt.Println(foo())
}

// 輸出:
// panic occurred msg: runtime error: invalid memory address or nil pointer dereference
// stack: runtime error: invalid memory address or nil pointer dereference
// main.handlePanic
//        /tmp/sandbox239055659/prog.go:19
// main.main.func1...

以上就是今天的內容!recover 函數并不是 Golang 開發者的日常必備工具,但正如你所看到的,它在某些情況下非常有用。

責任編輯:武曉燕 來源: 愛發白日夢的后端
相關推薦

2023-10-28 16:30:19

Golang開發

2023-10-26 12:05:14

Golang開發

2022-05-06 08:00:51

Golang編程語言Java

2020-08-20 10:16:56

Golang錯誤處理數據

2022-07-08 08:55:56

Go函數模型

2025-03-31 08:57:25

Go程序性能

2023-05-06 09:36:38

RecoverPanic

2025-03-18 09:20:00

Go語言Golang

2025-06-09 08:01:12

2021-01-14 21:37:01

JavaScript開發代碼

2021-10-28 19:21:56

GolangGo變量

2023-10-26 12:01:30

Golang字符串

2023-12-26 22:05:53

并發代碼goroutines

2021-03-02 07:31:26

WebApiweb

2020-10-29 06:02:44

PythonPandasExcel

2016-09-07 20:28:17

MySQL存儲數據庫

2024-04-16 12:18:05

編程異常處理錯誤返回

2025-02-10 09:49:00

2023-10-22 20:20:37

FiberGo

2025-06-09 01:15:00

點贊
收藏

51CTO技術棧公眾號

亚洲男同性视频| 久久激情五月丁香伊人| 日韩中文字幕一区二区| 色综合欧美在线| 亚洲图片都市激情| 亚洲精品3区| 亚洲人成电影网站色www| 天堂v视频永久在线播放| 国产麻豆成人传媒免费观看| 91精品国产自产在线| 国产精品麻豆| 日韩三级免费观看| 久久99精品国产99久久| 永久免费在线观看| 国产成人在线视频播放| 精品国产一二| 亚洲卡一卡二| 亚洲综合激情网| 日日鲁鲁鲁夜夜爽爽狠狠视频97| 亚洲欧美日韩国产一区| 日韩精品中文字幕一区二区三区| 好吊妞这里只有精品| 91在线视频在线| 9999在线观看| 日本亚洲三级在线| 免费观看成人高| 亚洲日本国产| 91嫩草在线| 亚洲精品极品少妇16p| 国产剧情日韩欧美| 精品国产91乱码一区二区三区四区 | 日本麻豆一区二区三区视频| 国产91在线高潮白浆在线观看| 91亚洲精品国产| 国产一区二区高清| 99www免费人成精品| 久久黄色美女电影| 黑人狂躁日本妞一区二区三区| h片免费观看| 国产精品色一区二区三区| 嫩草av久久伊人妇女超级a| 久久婷婷国产综合精品青草| 日韩美女在线观看| 精品国产一区二| 久热精品视频在线观看| 高清一区二区三区av| 欧美成人手机在线| 一级毛片在线播放| 欧美日韩国产在线| 国外av在线| 5月丁香婷婷综合| 性国产高清在线观看| 日韩三级在线免费观看| 国产传媒av在线| 在线观看亚洲视频| 91在线一区| 538在线一区二区精品国产| 欧美视频综合| 欧美老肥妇做.爰bbww| 国产在线更新| 亚洲精品综合久久中文字幕| 色在线免费观看| 亚洲一区二区三区爽爽爽爽爽| 成人免费观看网站| 国产亚洲福利| 亚洲欧美日韩不卡| 97se亚洲国产综合在线| 免费男女羞羞的视频网站中文版 | 亚洲一区二区三区在线免费| 亚洲一区二区高清| 中文字幕在线第一页| 欧美日韩在线不卡| 波多野结衣在线播放| 久久久精品美女| 成人在线免费观看91| 麻豆一区区三区四区产品精品蜜桃| 精东粉嫩av免费一区二区三区| 自慰无码一区二区三区| 午夜一区二区三区在线观看| 蜜乳av一区| 欧美一级大胆视频| 久久www成人_看片免费不卡| 91av资源网| 欧美在线观看一区| 国产精品尤物| 久艹视频在线免费观看| 亚洲国产成人91porn| 美女被啪啪一区二区| 国产一区二区按摩在线观看| 精品少妇无遮挡毛片| 91成人网在线| 日本在线精品| 国产欧美一区二区白浆黑人| 另类人妖一区二区av| 五月婷婷丁香综合网| 欧美在线视频日韩| 日韩色性视频| 99久久久精品免费观看国产| 国产精品性做久久久久久| 国产激情三区| 亚洲第一页自拍| 综合伊思人在钱三区| 亚洲国产精品一区二区第一页| 亚洲桃色在线一区| 色在线中文字幕| 97se视频在线观看| 国产欧美日本一区二区三区| 国产99视频精品免费视频36| 国产伦精一区二区三区| 日韩av在线中文| 精品日韩一区二区| 精品精品99| 欧美中日韩在线| 欧美在线观看视频在线| 综合视频一区| 一本二本三本亚洲码| 色婷婷精品大视频在线蜜桃视频 | 国产精品男人爽免费视频1| 国产一区在线不卡| 黄色毛片在线看| 777精品视频| 国产成人免费视频| 黄色片在线看| 国产精品7m视频| 久久综合久久99| 玖玖在线播放| 欧美精品v日韩精品v国产精品| 亚洲国产一区二区三区| 久久夜夜久久| 一级做a爰片久久| 欧美日韩中文国产| 精品盗摄女厕tp美女嘘嘘| 国产v亚洲v天堂无码久久久| 精品亚洲aⅴ在线观看| 国产视频一区免费看| 你懂的好爽在线观看| 国产成人精品午夜| 国产精品你懂的在线欣赏| 高清欧美日韩| 免费在线看黄色片| 国产亚洲一区二区在线| 免费视频最近日韩| 国产福利在线免费观看| 牛人盗摄一区二区三区视频| 欧美日韩亚洲综合一区| 亚洲午夜精品久久久久久app| 成人在线国产精品| 国产精品美女久久久久av爽李琼| 不卡av影片| 欧美做受777cos| 日韩精品视频免费在线观看| 日本亚洲免费观看| 毛片网站在线看| 日韩在线导航| 亚洲精品97久久| 国产精品中文字幕日韩精品| 我爱我色成人网| 久色视频在线播放| 九九热这里只有精品6| 国产色一区二区| 国内露脸中年夫妇交换精品| 亚洲视频第二页| 欧美中文字幕在线观看| 一区二区激情视频| 亚洲影视一区| 永久免费av片在线观看全网站| 国产精品一区二区三区免费| 欧美日韩高清不卡| 日本91福利区| 国精产品一区一区三区四川| 91在线视频精品| 男人的天堂avav| 92国产在线视频| 久久一区欧美| 欧美精品久久99久久在免费线 | 精久久久久久久久久久| 欧美一级播放| 色老汉一区二区三区| 午夜精品一区二区三区在线| 亚洲精品高清视频| 亚洲嫩模一区| 成人午夜一级| 国产精品普通话对白| 91福利区一区二区三区| 中文一区二区视频| 99国产视频| 香蕉视频在线免费| 国产免费不卡| 天堂在线一区二区| 日韩欧美在线视频日韩欧美在线视频| 久久精视频免费在线久久完整在线看| 91欧美精品成人综合在线观看| 国产裸体舞一区二区三区| 欧美xxxx视频| 99伊人成综合| 欧美综合一区二区三区| 欧美激情按摩在线| 色播五月综合| 美女精品视频| 久久av老司机精品网站导航|