Golang?并發(fā)下的問題定位及解決方案
問題描述
在使用 gin-swagger 的過程中, 經(jīng)常會發(fā)生因?yàn)槿鄙?json 等 tag 而導(dǎo)致的異常。 由于 gin-swagger 是并發(fā)執(zhí)行的, 輸出的日志本身是錯位的。 這就導(dǎo)致無法定義是哪一個結(jié)構(gòu)體缺少 tag 導(dǎo)致的。

一般而言, 這種時候只能一個個點(diǎn)開去檢查。
解決方案
思路 : 要是每行日志帶當(dāng)前 goroutine_id 的話, 是不是就可以準(zhǔn)確定位到報(bào)錯的 goroutine 他打印的日志是哪些了呢!
說做就做
實(shí)現(xiàn)思路
- 查看當(dāng)前日志是怎么打印的
發(fā)現(xiàn) gin-swagger 日志直接調(diào)用的 golang 的標(biāo)準(zhǔn)庫 log
由于沒有對log初始化, 所以默認(rèn)使用的是 stdout 。
log.Printf("Picking operation from %s\n", aurora.Blue(funType.FullName()))- 如果想要給日志中添加
goroutine_id的話, 就需要在調(diào)用log.Printf的時候獲取當(dāng)前goroutine的 id , 所以首先要解決的是怎么獲取goroutine_id的問題。
調(diào)研后發(fā)現(xiàn)了集中常見的獲取 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的結(jié)構(gòu)也可能不同, 所以此處我直接調(diào)用一個開源實(shí)現(xiàn):
https://github.com/petermattis/goid
- 修改
gin-swaggermain.go源碼, 修改logWrite的實(shí)現(xiàn)
修改前
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 調(diào)用 log 打印日志都時候, 都會使用我定義的 Write 方法, Write 方法中每次打印前都會先查詢出當(dāng)前的 goroutine_id ,然后作為日志的前綴。
修改后的日志效果

從中可以看到每行日志開頭,都已經(jīng)加上了 goroutine_id。 只要使用 panic goroutine 的 id 做一次 grep , 即可篩選出需要的日志了,極大的方便了定位問題。

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

到此這篇關(guān)于Golang 并發(fā)下的問題定位的文章就介紹到這了,更多相關(guān)Golang并發(fā)問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go?zero微服務(wù)實(shí)戰(zhàn)系服務(wù)拆分
這篇文章主要為大家介紹了go?zero微服務(wù)實(shí)戰(zhàn)系服務(wù)拆分的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Go?Excelize?API源碼解析GetSheetFormatPr使用示例
這篇文章主要為大家介紹了Go?Excelize?API源碼解析GetSheetFormatPr使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
使用Golang?Validator包實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證詳解
在開發(fā)過程中,數(shù)據(jù)驗(yàn)證是一個非常重要的環(huán)節(jié),而golang中的Validator包是一個非常常用和強(qiáng)大的數(shù)據(jù)驗(yàn)證工具,提供了簡單易用的API和豐富的驗(yàn)證規(guī)則,下面我們就來看看Validator包的具體使用吧2023-12-12
go 異常處理panic和recover的簡單實(shí)踐
在Go語言中,異常處理主要通過panic和recover這兩個內(nèi)建函數(shù)來實(shí)現(xiàn),本文主要介紹了go異常處理panic和recover的簡單實(shí)踐,具有一定的參考價值,感興趣的可以了解一下2025-04-04

