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项目中使用init()
Apr 12 Golang
golang日志包logger的用法详解
May 05 Golang
Go标准容器之Ring的使用说明
May 05 Golang
Golang生成Excel文档的方法步骤
Jun 09 Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 Golang
修改并编译golang源码的操作步骤
Jul 25 Golang
golang中的struct操作
Nov 11 Golang
一文搞懂Golang 时间和日期相关函数
Dec 06 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
Golang使用Panic与Recover进行错误捕获
Mar 22 Golang
如何解决goland,idea全局搜索快捷键失效问题
Apr 03 Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 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提交后跳转
2013/06/23 PHP
让div层随鼠标移动的实现代码 ie ff
2009/12/18 Javascript
用原生JS获取CLASS对象(很简单实用)
2014/10/15 Javascript
jQuery选择器querySelector的使用指南
2015/01/23 Javascript
JavaScript实现图片DIV竖向滑动的方法
2015/04/25 Javascript
js实现div层缓慢收缩与展开的方法
2015/05/11 Javascript
JavaScript判断按钮被点击的方法
2015/12/13 Javascript
angular动态删除ng-repaeat添加的dom节点的方法
2017/07/20 Javascript
详谈js对url进行编码和解码(三种方式的区别)
2017/08/16 Javascript
基于JavaScript实现简单扫雷游戏
2021/01/02 Javascript
python使用reportlab实现图片转换成pdf的方法
2015/05/22 Python
简单学习Python多进程Multiprocessing
2017/08/29 Python
Python星号*与**用法分析
2018/02/02 Python
python 实现登录网页的操作方法
2018/05/11 Python
Python线程之定位与销毁的实现
2019/02/17 Python
python读取Excel表格文件的方法
2019/09/02 Python
keras多显卡训练方式
2020/06/10 Python
为什么python比较流行
2020/06/19 Python
Django中使用Celery的方法步骤
2020/12/07 Python
H5 canvas中width、height和style的宽高区别详解
2018/11/02 HTML / CSS
Html5 localStorage入门教程
2018/04/26 HTML / CSS
澳洲健康食品网上商店:Aussie Health Products
2018/06/15 全球购物
TIME时代杂志台湾总代理:台时亚洲
2018/10/22 全球购物
中国电子产品批发商/跨境电商/外贸网:Sunsky-online
2020/04/20 全球购物
设置器与访问器的定义以及各自特点
2016/01/08 面试题
保险专业求职信
2014/07/07 职场文书
敬老月活动总结
2014/08/28 职场文书
小学生教师节演讲稿
2014/09/03 职场文书
企业委托书范本
2014/09/13 职场文书
乡镇党委书记第三阶段个人整改措施
2014/09/16 职场文书
银行授权委托书格式
2014/10/10 职场文书
长城导游词300字
2015/01/30 职场文书
2015年党员公开承诺事项
2015/04/27 职场文书
Python基础之操作MySQL数据库
2021/05/06 Python
判断Python中的Nonetype类型
2021/05/25 Python
详解Go与PHP的语法对比
2021/05/29 PHP