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

詳解如何在Go服務中做鏈路追蹤

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

使用 Go 語言開發(fā)微服務的時候,需要追蹤每一個請求的訪問鏈路,這塊在 Go 中目前沒有很好的解決方案。

在 Java 中解決這個問題比較簡單,可以使用 MDC,在一個進程內共享一個請求的 RequestId。

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

下面的代碼基于 gin 框架來實現(xiàn)。

1. 使用全局 map 來實現(xiàn)

使用 map 方案需要在全局維護一個 map,在一個請求進來的時候,會為每一個請求生成 RequestId,然后在每次在打印日志的時候,從這個 Map 中通過 goid 獲取到 RequestId,打印到日志中。

代碼的實現(xià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() // 在日志中間件中為每個請求設定
        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) // 打印日志
}

這樣的實現(xiàn)很簡單,但是問題也很多。

第一個問題就是,在 Go 程序中,一次請求可能會涉及到多個 goroutine,用這種方式很難在多個 gotoutine 之間傳遞 RequestId。

在下面的代碼中,如果新啟動了一個 goroutine,就會導致日志中獲取不到 RequestId:

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

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

        go func() {  // 這里新啟動了一個一個 goroutine
            Info("goroutine1")
        }()

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

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

在每個請求結束的時候,還需要手動的把 requestId 從 map 中刪除,否則就會造成內存泄漏。

總的來說,使用 map 這種方式來實現(xiàn)并不是很好。

2. 使用 Context 來實現(xiàn)

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

在傳遞 RequestId 的場景中,同樣也可以使用 Context 來實現(xiàn),使用 Context 好處很明顯,Context 生命周期與請求相同,不需要手動銷毀。而且Context 是每個請求獨享的,也不用擔心并發(fā)安全的問題,Context 還可以在 goroutine 之間傳遞。

使用 Context 實現(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)
}

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

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

Context 除了使用可以同來傳遞 RequestId 之外,還可以用來控制 goroutine 的生命周期,這些內容在之前的 Context 文章中詳細說明了,感興趣的可以去看看。

3. 小結

獲取 goroutine id 這種方式應該被拋棄,而是應該使用 Context, Go 官方也早就推薦使用這種方式,在上文中,我們使用 Context 來傳遞 RequestId,除此之外還可以用來傳遞單個請求范圍的值,比如認證的 token 之類的,應該習慣在代碼中使用 Context。

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

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

相關文章

  • 一文詳解在Go中如何使用Viper來管理配置

    一文詳解在Go中如何使用Viper來管理配置

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

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

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

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

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

    Go語言與其他語言進行交互的方式詳解

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

    Go語言panic和recover的用法實例

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

    Go語言多人聊天室項目實戰(zhàn)

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

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

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

    詳解Go語言中接口應用模式或慣例介紹

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

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

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

    一文吃透Go的內置RPC原理

    這篇文章主要為大家詳細介紹了Go語言中內置RPC的原理。說起?RPC?大家想到的一般是框架,Go?作為編程語言竟然還內置了?RPC,著實讓我有些吃鯨,本文就來一起聊聊吧
    2023-03-03

最新評論