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

詳解如何在Go服務(wù)中做鏈路追蹤

 更新時(shí)間:2021年09月29日 10:48:11   作者:rayjun  
使用 Go 語(yǔ)言開(kāi)發(fā)微服務(wù)的時(shí)候,需要追蹤每一個(gè)請(qǐng)求的訪問(wèn)鏈路,本文主要介紹了如何在Go 服務(wù)中做鏈路追蹤,感興趣的可以了解一下

使用 Go 語(yǔ)言開(kāi)發(fā)微服務(wù)的時(shí)候,需要追蹤每一個(gè)請(qǐng)求的訪問(wèn)鏈路,這塊在 Go 中目前沒(méi)有很好的解決方案。

在 Java 中解決這個(gè)問(wèn)題比較簡(jiǎn)單,可以使用 MDC,在一個(gè)進(jìn)程內(nèi)共享一個(gè)請(qǐng)求的 RequestId。

在 Go 中實(shí)現(xiàn)鏈路追蹤有兩種思路:一種是在項(xiàng)目中使用一個(gè)全局的 map, key 是 goroutine 的唯一 Id,value 是 RequestId,另一種思路可以使用 context.Context 來(lái)實(shí)現(xiàn)。

下面的代碼基于 gin 框架來(lái)實(shí)現(xiàn)。

1. 使用全局 map 來(lái)實(shí)現(xiàn)

使用 map 方案需要在全局維護(hù)一個(gè) map,在一個(gè)請(qǐng)求進(jìn)來(lái)的時(shí)候,會(huì)為每一個(gè)請(qǐng)求生成 RequestId,然后在每次在打印日志的時(shí)候,從這個(gè) Map 中通過(guò) goid 獲取到 RequestId,打印到日志中。

代碼的實(shí)現(xiàn)很簡(jiǎn)單:

var requestIdMap = make(map[int64]string) // 全局的 Map

func main() {
    r := gin.Default()
    r.Use(Logger()) // 使用中間件

    r.GET("/index", func(c *gin.Context) {
        Info("main goroutine") // 打印日志

        c.JSON(200, gin.H{
            "message": "index",
        })
    })
    r.Run()
}

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestIdMap[goid.Get()] = uuid.New().String() // 在日志中間件中為每個(gè)請(qǐng)求設(shè)定
        c.Next()
    }
}

func Info(msg string)  {
    now := time.Now()
    nowStr := now.Format("2006-01-02 15:04:05")
    fmt.Printf("%s [%s] %s\n", nowStr, requestIdMap[goid.Get()], msg) // 打印日志
}

這樣的實(shí)現(xiàn)很簡(jiǎn)單,但是問(wèn)題也很多。

第一個(gè)問(wèn)題就是,在 Go 程序中,一次請(qǐng)求可能會(huì)涉及到多個(gè) goroutine,用這種方式很難在多個(gè) gotoutine 之間傳遞 RequestId。

在下面的代碼中,如果新啟動(dòng)了一個(gè) goroutine,就會(huì)導(dǎo)致日志中獲取不到 RequestId:

func main() {
    r := gin.Default()
    r.Use(Logger())

    r.GET("/index", func(c *gin.Context) {
        Info("main goroutine")

        go func() {  // 這里新啟動(dòng)了一個(gè)一個(gè) goroutine
            Info("goroutine1")
        }()

        c.JSON(200, gin.H{
            "message": "index",
        })
    })
    r.Run()
}

獲取 goroutine id 也不是一種常規(guī)的做法,一般要通過(guò) hack 的方式來(lái)獲取,這種做法已經(jīng)不推薦了。而且這個(gè)全局的 map 為了并發(fā)安全,在實(shí)際的使用中,可以還需要用到鎖,在高并發(fā)的情況下必然會(huì)影響性能。

在每個(gè)請(qǐng)求結(jié)束的時(shí)候,還需要手動(dòng)的把 requestId 從 map 中刪除,否則就會(huì)造成內(nèi)存泄漏。

總的來(lái)說(shuō),使用 map 這種方式來(lái)實(shí)現(xiàn)并不是很好。

2. 使用 Context 來(lái)實(shí)現(xiàn)

在上面的代碼中,我們使用一個(gè) hack 的方式去獲取 goroutine id,這種方式早就不推薦使用,更推薦使用 Context,關(guān)于 Context 內(nèi)容,可以去看我之前的文章,在這里就不多說(shuō)了。

在傳遞 RequestId 的場(chǎng)景中,同樣也可以使用 Context 來(lái)實(shí)現(xiàn),使用 Context 好處很明顯,Context 生命周期與請(qǐng)求相同,不需要手動(dòng)銷(xiāo)毀。而且Context 是每個(gè)請(qǐng)求獨(dú)享的,也不用擔(dān)心并發(fā)安全的問(wèn)題,Context 還可以在 goroutine 之間傳遞。

使用 Context 實(shí)現(xiàn)的代碼如下:

func main() {
    r := gin.Default()
    r.Use(Logger())

    r.GET("/index", func(c *gin.Context) {

        ctx, _ := c.Get("ctx")

        Info(ctx.(context.Context) , "main goroutine")

        go func() {
            Info(ctx.(context.Context), "goroutine1")
        }()

        c.JSON(200, gin.H{
            "message": "index",
        })
    })
    r.Run()
}

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        valueCtx := context.WithValue(c.Request.Context(), "RequestId", uuid.New().String())
        c.Set("ctx", valueCtx)
        c.Next()
    }
}

func Info(ctx context.Context, msg string)  {
    now := time.Now()
    nowStr := now.Format("2006-01-02 15:04:05")
    fmt.Printf("%s [%s] %s\n", nowStr, ctx.Value("RequestId"), msg)
}

這樣在一個(gè)請(qǐng)求中,所有的 gotroutine 都可以獲取到同一個(gè) RequestId,而且不用擔(dān)心內(nèi)存泄漏和并發(fā)安全。

但是使用 Context 也有個(gè)問(wèn)題就是需要每次傳遞 Context,很多人還不習(xí)慣使用這種方式。其實(shí) Go 官方早就推薦使用 Context了,通常會(huì)把 Context 作為函數(shù)的第一個(gè)參數(shù)。如果函數(shù)使用結(jié)構(gòu)體作為參數(shù),也可以直接把 Context 作為結(jié)構(gòu)體的一個(gè)字段。

Context 除了使用可以同來(lái)傳遞 RequestId 之外,還可以用來(lái)控制 goroutine 的生命周期,這些內(nèi)容在之前的 Context 文章中詳細(xì)說(shuō)明了,感興趣的可以去看看。

3. 小結(jié)

獲取 goroutine id 這種方式應(yīng)該被拋棄,而是應(yīng)該使用 Context, Go 官方也早就推薦使用這種方式,在上文中,我們使用 Context 來(lái)傳遞 RequestId,除此之外還可以用來(lái)傳遞單個(gè)請(qǐng)求范圍的值,比如認(rèn)證的 token 之類(lèi)的,應(yīng)該習(xí)慣在代碼中使用 Context。

[1] https://blog.golang.org/context

到此這篇關(guān)于詳解如何在Go 服務(wù)中做鏈路追蹤的文章就介紹到這了,更多相關(guān)Go 服務(wù)中做鏈路追蹤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文詳解在Go中如何使用Viper來(lái)管理配置

    一文詳解在Go中如何使用Viper來(lái)管理配置

    Viper 是一個(gè)功能齊全的 Go 應(yīng)用程序配置庫(kù),支持很多場(chǎng)景。在本文中,我們將深入探討 Viper 的各種用法和使用場(chǎng)景,以幫助讀者更好地了解和使用 Viper 來(lái)管理應(yīng)用程序配置,感興趣的同學(xué)可以參考閱讀
    2023-05-05
  • 使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能(demo)

    使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能(demo)

    這篇文章主要介紹了使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Go語(yǔ)言實(shí)現(xiàn)UDP版聊天小工具的示例詳解

    Go語(yǔ)言實(shí)現(xiàn)UDP版聊天小工具的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Go語(yǔ)言實(shí)現(xiàn)聊天小工具(UDP版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Go語(yǔ)言與其他語(yǔ)言進(jìn)行交互的方式詳解

    Go語(yǔ)言與其他語(yǔ)言進(jìn)行交互的方式詳解

    在當(dāng)今的軟件開(kāi)發(fā)領(lǐng)域,多種編程語(yǔ)言常常需要協(xié)同工作,以充分利用各自的優(yōu)勢(shì)來(lái)構(gòu)建復(fù)雜的應(yīng)用系統(tǒng),Go 語(yǔ)言作為一門(mén)高效、簡(jiǎn)潔的編程語(yǔ)言,也經(jīng)常需要與其他語(yǔ)言進(jìn)行交互,接下來(lái),我們將詳細(xì)探討 Go 語(yǔ)言如何與其他語(yǔ)言進(jìn)行交互,需要的朋友可以參考下
    2024-06-06
  • Go語(yǔ)言panic和recover的用法實(shí)例

    Go語(yǔ)言panic和recover的用法實(shí)例

    panic()和recover()是Go語(yǔ)言中用于處理錯(cuò)誤的兩個(gè)重要函數(shù),本文主要介紹了Go語(yǔ)言panic和recover的用法實(shí)例,panic()用于中止程序并引發(fā)panic,而recover()用于捕獲panic并恢復(fù)程序的執(zhí)行,感興趣的可以了解一下
    2024-01-01
  • Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn)

    Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn)

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn),實(shí)現(xiàn)單撩或多撩等多種功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Golang切片和數(shù)組拷貝詳解(淺拷貝和深拷貝)

    Golang切片和數(shù)組拷貝詳解(淺拷貝和深拷貝)

    這篇文章主要為大家詳細(xì)介紹一下Golang切片拷貝和數(shù)組拷貝,文中有詳細(xì)的代碼示例供大家參考,需要的可以參考一下
    2023-04-04
  • 詳解Go語(yǔ)言中接口應(yīng)用模式或慣例介紹

    詳解Go語(yǔ)言中接口應(yīng)用模式或慣例介紹

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中接口應(yīng)用模式或慣例介紹的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • Go語(yǔ)言動(dòng)態(tài)并發(fā)控制sync.WaitGroup的靈活運(yùn)用示例詳解

    Go語(yǔ)言動(dòng)態(tài)并發(fā)控制sync.WaitGroup的靈活運(yùn)用示例詳解

    本文將講解 sync.WaitGroup 的使用方法、原理以及在實(shí)際項(xiàng)目中的應(yīng)用場(chǎng)景,用清晰的代碼示例和詳細(xì)的注釋,助力讀者掌握并發(fā)編程中等待組的使用技巧
    2023-11-11
  • 一文吃透Go的內(nèi)置RPC原理

    一文吃透Go的內(nèi)置RPC原理

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中內(nèi)置RPC的原理。說(shuō)起?RPC?大家想到的一般是框架,Go?作為編程語(yǔ)言竟然還內(nèi)置了?RPC,著實(shí)讓我有些吃鯨,本文就來(lái)一起聊聊吧
    2023-03-03

最新評(píng)論