Golang 并发下的问题定位及解决方案


Posted in Golang onMarch 16, 2022

问题描述

在使用 gin-swagger 的过程中, 经常会发生因为缺少 jsontag 而导致的异常。 由于 gin-swagger 是并发执行的, 输出的日志本身是错位的。 这就导致无法定义是哪一个结构体缺少 tag 导致的。

Golang 并发下的问题定位及解决方案

一般而言, 这种时候只能一个个点开去检查。

解决方案

思路 : 要是每行日志带当前 goroutine_id 的话, 是不是就可以准确定位到报错的 goroutine 他打印的日志是哪些了呢!

说做就做

实现思路

  • 查看当前日志是怎么打印的

发现 gin-swagger 日志直接调用的 golang 的标准库 log

由于没有对log初始化, 所以默认使用的是 stdout

log.Printf("Picking operation from %s\n", 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 ,然后作为日志的前缀。

修改后的日志效果

Golang 并发下的问题定位及解决方案

从中可以看到每行日志开头,都已经加上了 goroutine_id。 只要使用 panic goroutineid 做一次 grep , 即可筛选出需要的日志了,极大的方便了定位问题。

Golang 并发下的问题定位及解决方案

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

Golang 并发下的问题定位及解决方案

到此这篇关于Golang 并发下的问题定位的文章就介绍到这了,更多相关Golang并发问题内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
Go各时间字符串使用解析
Apr 02 Golang
Go语言 go程释放操作(退出/销毁)
Apr 30 Golang
解决golang结构体tag编译错误的问题
May 02 Golang
go语言中GOPATH GOROOT的作用和设置方式
May 05 Golang
golang switch语句的灵活写法介绍
May 06 Golang
golang 实现时间戳和时间的转化
May 07 Golang
go web 预防跨站脚本的实现方式
Jun 11 Golang
Golang的继承模拟实例
Jun 30 Golang
Go语言空白表示符_的实例用法
Jul 04 Golang
浅谈GO中的Channel以及死锁的造成
Mar 18 Golang
Golang数据类型和相互转换
Apr 12 Golang
Golang MatrixOne使用介绍和汇编语法
Apr 19 Golang
如何利用golang运用mysql数据库
深入理解go缓存库freecache的使用
Feb 15 #Golang
Go语言读取txt文档的操作方法
Jan 22 #Golang
一文搞懂Golang 时间和日期相关函数
Go语言基础切片的创建及初始化示例详解
Nov 17 #Golang
Go语言基础map用法及示例详解
Nov 17 #Golang
Go语言基础函数基本用法及示例详解
Nov 17 #Golang
You might like
PHP数据缓存技术
2007/02/14 PHP
php下用GD生成生成缩略图的两个选择和区别
2007/04/17 PHP
ThinkPHP3.1新特性之多数据库操作更加完善
2014/06/19 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十三)
2014/06/26 PHP
在WordPress中实现发送http请求的相关函数解析
2015/12/29 PHP
php+jquery+html实现点击不刷新加载更多的实例代码
2016/08/12 PHP
php json中文编码为null的解决办法
2016/12/14 PHP
php实现自定义中奖项数和概率的抽奖函数示例
2017/05/26 PHP
JavaScript中也使用$美元符号来代替document.getElementById
2010/06/19 Javascript
验证码按回车不变解决方法
2013/03/29 Javascript
js使用数组判断提交数据是否存在相同数据
2013/11/27 Javascript
Javascript中的异步编程规范Promises/A详细介绍
2014/06/06 Javascript
JS定义网页表单提交(submit)的方法
2015/03/20 Javascript
JavaScript中for循环的使用详解
2015/06/03 Javascript
简介JavaScript中的setTime()方法的使用
2015/06/11 Javascript
js轮播图透明度切换(带上下页和底部圆点切换)
2017/04/27 Javascript
Node.js环境下Koa2添加travis ci持续集成工具的方法
2017/06/19 Javascript
js经验分享 JavaScript反调试技巧
2018/03/10 Javascript
ndm:NPM的桌面GUI应用程序
2018/10/15 Javascript
JavaScript实现的拼图算法分析
2019/02/13 Javascript
vue中实现上传文件给后台实例详解
2019/08/22 Javascript
微信小程序实现音频文件播放进度的实例代码
2020/03/02 Javascript
[02:38]DOTA2 夜魇暗潮2020活动介绍官方视频
2020/11/04 DOTA
举例讲解Python中的Null模式与桥接模式编程
2016/02/02 Python
python中zip()方法应用实例分析
2016/04/16 Python
Python实现一个Git日志统计分析的小工具
2017/12/14 Python
pandas 两列时间相减换算为秒的方法
2018/04/20 Python
使用PyQt4 设置TextEdit背景的方法
2019/06/14 Python
Python图像处理模块ndimage用法实例分析
2019/09/05 Python
python3 assert 断言的使用详解 (区别于python2)
2019/11/27 Python
Python爬虫之Selenium警告框(弹窗)处理
2020/12/04 Python
澳大利亚领先的在线药房:Pharmacy Online(有中文站)
2020/02/22 全球购物
莫斯科制造商的廉价皮大衣:Fursk
2020/06/09 全球购物
打架检讨书范文
2015/01/27 职场文书
浅谈redis缓存在项目中的使用
2021/05/20 Redis
Python实现将多张图片合成MP4视频并加入背景音乐
2022/04/28 Python