最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

在 Java 中解決這個(gè)問題比較簡(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 來實(shí)現(xiàn)。

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

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

使用 map 方案需要在全局維護(hù)一個(gè) map,在一個(gè)請(qǐng)求進(jìn)來的時(shí)候,會(huì)為每一個(gè)請(qǐng)求生成 RequestId,然后在每次在打印日志的時(shí)候,從這個(gè) Map 中通過 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)單,但是問題也很多。

第一個(gè)問題就是,在 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ī)的做法,一般要通過 hack 的方式來獲取,這種做法已經(jīng)不推薦了。而且這個(gè)全局的 map 為了并發(fā)安全,在實(shí)際的使用中,可以還需要用到鎖,在高并發(fā)的情況下必然會(huì)影響性能。

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

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

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

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

在傳遞 RequestId 的場(chǎng)景中,同樣也可以使用 Context 來實(shí)現(xiàn),使用 Context 好處很明顯,Context 生命周期與請(qǐng)求相同,不需要手動(dòng)銷毀。而且Context 是每個(gè)請(qǐng)求獨(dú)享的,也不用擔(dān)心并發(fā)安全的問題,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è)問題就是需要每次傳遞 Context,很多人還不習(xí)慣使用這種方式。其實(shí) Go 官方早就推薦使用 Context了,通常會(huì)把 Context 作為函數(shù)的第一個(gè)參數(shù)。如果函數(shù)使用結(jié)構(gòu)體作為參數(shù),也可以直接把 Context 作為結(jié)構(gòu)體的一個(gè)字段。

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

3. 小結(jié)

獲取 goroutine id 這種方式應(yīng)該被拋棄,而是應(yīng)該使用 Context, Go 官方也早就推薦使用這種方式,在上文中,我們使用 Context 來傳遞 RequestId,除此之外還可以用來傳遞單個(gè)請(qǐng)求范圍的值,比如認(rèn)證的 token 之類的,應(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)文章

  • 一文詳解kubernetes?中資源分配的那些事

    一文詳解kubernetes?中資源分配的那些事

    這篇文章主要為大家介紹了kubernetes?中資源分配的那些事,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Go語言類型內(nèi)嵌和結(jié)構(gòu)體內(nèi)嵌的具體使用

    Go語言類型內(nèi)嵌和結(jié)構(gòu)體內(nèi)嵌的具體使用

    本文主要介紹了Go語言類型內(nèi)嵌和結(jié)構(gòu)體內(nèi)嵌的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go語言標(biāo)準(zhǔn)輸入輸出庫的基本使用教程

    Go語言標(biāo)準(zhǔn)輸入輸出庫的基本使用教程

    輸入輸出在任何一門語言中都必須提供的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Go語言標(biāo)準(zhǔn)輸入輸出庫的基本使用,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • gin?session中間件使用及源碼流程分析

    gin?session中間件使用及源碼流程分析

    這篇文章主要為大家介紹了gin?session中間件使用及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Go語言中的并發(fā)goroutine底層原理

    Go語言中的并發(fā)goroutine底層原理

    這篇文章主要介紹了Go語言中的并發(fā)goroutine底層原理,介紹Go語言并發(fā)底層原理,以及對(duì)比Go語言并發(fā)與其他語言并發(fā)的優(yōu)劣,下文詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • golang fmt格式“占位符”的實(shí)例用法詳解

    golang fmt格式“占位符”的實(shí)例用法詳解

    在本篇文章里小編給大家整理的是一篇關(guān)于golang fmt格式“占位符”的實(shí)例用法詳解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-07-07
  • Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    在 Golang 中,經(jīng)常需要將其他類型(例如 slice、map、struct 等類型)的數(shù)據(jù)轉(zhuǎn)化為 JSON 格式,有時(shí)候轉(zhuǎn)化的結(jié)果并不是預(yù)期中的,例如將一個(gè)空的切片轉(zhuǎn)化為 JSON 時(shí),會(huì)變成"null",所以本文將給大家介紹一下解決方法,需要的朋友可以參考下
    2023-09-09
  • Go 中閉包的底層原理

    Go 中閉包的底層原理

    這篇文章主要介紹了Go 中閉包的底層原理,閉包的基本原理是一種現(xiàn)象,一個(gè)函數(shù)內(nèi)引用了外部的局部變量的現(xiàn)象,帶著些許的了解和小編一起進(jìn)入文章正題學(xué)習(xí)
    2021-10-10
  • golang 自旋鎖的實(shí)現(xiàn)

    golang 自旋鎖的實(shí)現(xiàn)

    這篇文章主要介紹了golang 自旋鎖的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • idea搭建go環(huán)境實(shí)現(xiàn)go語言開發(fā)

    idea搭建go環(huán)境實(shí)現(xiàn)go語言開發(fā)

    這篇文章主要給大家介紹了關(guān)于idea搭建go環(huán)境實(shí)現(xiàn)go語言開發(fā)的相關(guān)資料,文中通過圖文介紹以及代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用go具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

蒙山县| 炎陵县| 乐业县| 中卫市| 侯马市| 武穴市| 和林格尔县| 肃宁县| 特克斯县| 绩溪县| 肃宁县| 铜陵市| 屯留县| 广元市| 离岛区| 彭泽县| 慈利县| 台中市| 静安区| 祁阳县| 义马市| 崇义县| 荣成市| 松滋市| 潜江市| 柘荣县| 宜川县| 天峨县| 汝南县| 东安县| 青冈县| 杂多县| 德江县| 华安县| 岐山县| 老河口市| 新野县| 屏东县| 内丘县| 美姑县| 榆树市|