首頁 > 軟體

Golang 並行下的問題定位及解決方案

2022-03-15 19:00:43

問題描述

在使用 gin-swagger 的過程中, 經常會發生因為缺少 jsontag 而導致的異常。 由於 gin-swagger 是並行執行的, 輸出的紀錄檔本身是錯位的。 這就導致無法定義是哪一個結構體缺少 tag 導致的。

一般而言, 這種時候只能一個個點開去檢查。

解決方案

思路 : 要是每行紀錄檔帶當前 goroutine_id 的話, 是不是就可以準確定位到報錯的 goroutine 他列印的紀錄檔是哪些了呢!

說做就做

實現思路

  • 檢視當前紀錄檔是怎麼列印的

發現 gin-swagger 紀錄檔直接呼叫的 golang 的標準庫 log

由於沒有對log初始化, 所以預設使用的是 stdout

log.Printf("Picking operation from %sn", aurora.Blue(funType.FullName()))
  • 如果想要給紀錄檔中新增 goroutine_id 的話, 就需要在呼叫 log.Printf 的時候獲取當前 goroutine 的 id , 所以首先要解決的是怎麼獲取 goroutine_id 的問題。

調研後發現了集中常見的獲取 goroutine_id 的方法:

2.1 通過棧資訊解析後獲取

func GetGID() uint64 {
    b := make([]byte, 64)
    b = b[:runtime.Stack(b, false)]
    b = bytes.TrimPrefix(b, []byte("goroutine "))
    b = b[:bytes.IndexByte(b, ' ')]
    n, _ := strconv.ParseUint(string(b), 10, 64)
    return n
}

2.2 修改 Go 原始碼獲取

# src/runtime/runtime2.go
func Goid() int64 {
    _g_ := getg()
    return _g_.goid
}

2.3 通過 CGO 獲取

檔案 id.c

#include "runtime.h"
int64 ·Id(void) {
    return g->goid;
}

檔案 id.go

package id
func Id() int64

另外還可以通過組合獲取 goroutine_id

由於go的版本不同,goroutine的結構也可能不同, 所以此處我直接呼叫一個開源實現:

https://github.com/petermattis/goid

  • 修改 gin-swagger main.go 原始碼, 修改 log Write 的實現

修改前

func main() {
    cmd.Execute()
}

修改後

type Out os.File
func (o Out) Write(b []byte) (int, error) {
    prefix := fmt.Sprintf("gid-%d: ", goid.Get())
    all := make([]byte, len(b)+len(prefix))
    all = []byte(prefix)
    all = append(all, b...)
    f := os.File(o)
    return f.Write(all)
}
func main() {
    var out Out
    out = Out(os.Stdout)
    log.SetOutput(out)
    cmd.Execute()

這樣修改後,每次 gin-swagger 呼叫 log 列印紀錄檔都時候, 都會使用我定義的 Write 方法, Write 方法中每次列印前都會先查詢出當前的 goroutine_id ,然後作為紀錄檔的字首。

修改後的紀錄檔效果

從中可以看到每行紀錄檔開頭,都已經加上了 goroutine_id。 只要使用 panic goroutineid 做一次 grep , 即可篩選出需要的紀錄檔了,極大的方便了定位問題。

cat /tmp/gin-swagger.log | grep 7647

到此這篇關於Golang 並行下的問題定位的文章就介紹到這了,更多相關Golang並行問題內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com