欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang Recover處理錯(cuò)誤原理解析

 更新時(shí)間:2023年12月01日 11:02:25   作者:TimLiu 愛(ài)發(fā)白日夢(mèng)的后端  
Golang 中的?recover?是一個(gè)鮮為人知但非常有趣和強(qiáng)大的功能,讓我們看看它是如何工作的,以及在 Outreach.io 中如何利用它來(lái)處理 Kubernetes 中的錯(cuò)誤

引言

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

Defer

要充分理解 recover,我們首先需要談?wù)?nbsp;defer 語(yǔ)句。defer ;關(guān)鍵字前置于函數(shù)調(diào)用之前,使得該調(diào)用在當(dāng)前函數(shù)返回之前執(zhí)行。當(dāng)我們?cè)谝粋€(gè)函數(shù)中使用多個(gè) defer 語(yǔ)句時(shí),它們按照后進(jìn)先出的順序執(zhí)行,這使得創(chuàng)建清理邏輯變得非常容易,如下例所示:

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() // 這個(gè)函數(shù)調(diào)用將在 readRecords 函數(shù)返回時(shí)第三個(gè)執(zhí)行
    conn, err := db.Conn(ctx)
    if err != nil {
        return err
    }
    defer conn.Close() // 這個(gè)函數(shù)調(diào)用將在第二個(gè)執(zhí)行
    rows, err := conn.QueryContext(ctx, "SELECT id FROM users")
    if err != nil {
        return err
    }
    defer rows.Close() // 這個(gè)函數(shù)調(diào)用將在第一個(gè)執(zhí)行
    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

我們需要談?wù)摰牡诙€(gè)主題是 panic,它是一個(gè)導(dǎo)致當(dāng)前 goroutine 進(jìn)入 panic 模式的函數(shù)。當(dāng)前函數(shù)中的正常執(zhí)行流程被停止,僅執(zhí)行 defer 語(yǔ)句,然后對(duì)調(diào)用者函數(shù)執(zhí)行相同的操作,因此一直冒泡到堆棧的頂部(main 函數(shù)),然后使程序崩潰。panic 可以直接調(diào)用(傳遞一個(gè)值作為參數(shù)),也可以由運(yùn)行時(shí)錯(cuò)誤引起。例如,由于空指針解引用:

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

Recover

recover 是一個(gè)內(nèi)建函數(shù),它使我們有可能在發(fā)生 panic 時(shí)重新獲得控制。它僅在被調(diào)用的延遲函數(shù)中產(chǎn)生效果。在延遲函數(shù)之外調(diào)用時(shí),它總是返回 nil。如果我們處于 panic 模式,調(diào)用 recover 會(huì)返回傳遞給 panic 函數(shù)的值?;臼纠?/p>

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

我們可以以同樣的方式從運(yùn)行時(shí)錯(cuò)誤中恢復(fù):

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 返回的值的類型是錯(cuò)誤(更準(zhǔn)確地說(shuō)是 runtime.errorString)。

有一個(gè)限制:我們不能直接從 recover 塊中返回值,因?yàn)樵?nbsp;recover 塊中的 return 語(yǔ)句僅從延遲函數(shù)中返回,而不是從周圍的函數(shù)中返回:

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" 因?yàn)槲覀儍H從匿名函數(shù)返回
        }
    }()
    panic("spam, egg, sausage, and spam")
}
func main() {
    x := foo()
    fmt.Println(x)
}

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

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

一個(gè)更實(shí)際的例子,將 panic 轉(zhuǎn)換為普通錯(cuò)誤的轉(zhuǎn)換可能如下所示:

package main
import (
    "fmt"
    "github.com/google/uuid"
)
// processInput 嘗試將輸入字符串轉(zhuǎn)換為 uuid.UUID
// 它將 panic 轉(zhuǎn)換為錯(cuò)誤
func processInput(input string) (u uuid.UUID, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v", r)
        }
    }()
    // 一些可能引發(fā) 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

現(xiàn)在讓我們嘗試一些稍微

復(fù)雜的東西。假設(shè)我們?cè)?Kubernetes 中運(yùn)行,并且我們想要編寫一個(gè)通用的 recover 函數(shù),處理所有未捕獲的 panic 和運(yùn)行時(shí)錯(cuò)誤,并收集它們的堆棧跟蹤,以便我們可以以結(jié)構(gòu)化的方式記錄它們(例如,以 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...

以上就是今天的內(nèi)容!recover 函數(shù)并不是 Golang 開(kāi)發(fā)者的日常必備工具,但正如你所看到的,它在某些情況下非常有用,更多關(guān)于Golang Recover錯(cuò)誤處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go中的fuzz模糊測(cè)試使用實(shí)戰(zhàn)詳解

    Go中的fuzz模糊測(cè)試使用實(shí)戰(zhàn)詳解

    這篇文章主要為大家介紹了Go中的fuzz模糊測(cè)試使用實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang常用手冊(cè)之切片(Slice)原理

    golang常用手冊(cè)之切片(Slice)原理

    本篇文章主要介紹了golang常用手冊(cè)之切片(Slice)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • golang雙鏈表的實(shí)現(xiàn)代碼示例

    golang雙鏈表的實(shí)現(xiàn)代碼示例

    這篇文章主要介紹了golang雙鏈表的實(shí)現(xiàn)代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Go語(yǔ)言Gin框架實(shí)現(xiàn)HTML頁(yè)面渲染

    Go語(yǔ)言Gin框架實(shí)現(xiàn)HTML頁(yè)面渲染

    Web開(kāi)發(fā)中,我們經(jīng)常要面對(duì)如何將數(shù)據(jù)渲染到前端的問(wèn)題,這就涉及到了模板引擎的知識(shí),Go語(yǔ)言的Gin框架就提供了強(qiáng)大的HTML模板渲染功能,本文就來(lái)為大家介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2024-01-01
  • Go基本數(shù)據(jù)類型與string類型互轉(zhuǎn)

    Go基本數(shù)據(jù)類型與string類型互轉(zhuǎn)

    本文主要介紹了Go基本數(shù)據(jù)類型與string類型互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆

    詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆

    堆是一種非常常用的數(shù)據(jù)結(jié)構(gòu),它能夠支持在O(1)的時(shí)間復(fù)雜度獲取到最大值(或最小值)。本文主要介紹了如何實(shí)現(xiàn)支持O(log(n))隨機(jī)刪除元素的堆,需要的可以參考一下
    2022-09-09
  • 使用Go語(yǔ)言實(shí)現(xiàn)常見(jiàn)hash算法

    使用Go語(yǔ)言實(shí)現(xiàn)常見(jiàn)hash算法

    這篇文章主要為大家詳細(xì)介紹了使語(yǔ)言實(shí)現(xiàn)各種常見(jiàn)hash算法的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考下
    2024-01-01
  • golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作

    golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作

    這篇文章主要介紹了golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Golang中時(shí)間格式化的實(shí)現(xiàn)詳解

    Golang中時(shí)間格式化的實(shí)現(xiàn)詳解

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中進(jìn)行時(shí)間進(jìn)行格式化的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-09-09
  • Golang使用Docker進(jìn)行集成測(cè)試的示例詳解

    Golang使用Docker進(jìn)行集成測(cè)試的示例詳解

    集成測(cè)試需要解決外部依賴問(wèn)題,如?MySQL、Redis、網(wǎng)絡(luò)等依賴,本文就來(lái)聊聊?Go?程序如何使用?Docker?來(lái)解決集成測(cè)試中外部依賴問(wèn)題吧
    2023-07-07

最新評(píng)論