详解如何在Go服务中做链路追踪

文章编号:5151 安全相关 2023-10-18

使用Go语言开发微服务的时候,需要追踪每一个请求的访问链路,这块在Go中目前没有很好的解决方案。

详解如何在Go服务中做链路追踪

在Java中解决这个问题比较简单,可以使用MDC,在一个进程内共享一个请求的RequestId。

在Go中实现链路追踪有两种思路:一种是在项目中使用一个全局的map,key是goroutine的唯一Id,value是RequestId,另一种思路可以使用context.Context来实现。

下面的代码基于gin框架来实现。

使用map方案需要在全局维护一个map,在一个请求进来的时候,会为每一个请求生成RequestId,然后在每次在打印日志的时候,从这个Map中通过goid获取到RequestId,打印到日志中。

代码的实现很简单:

varrequestIdMap=make(map[int64]string)//全局的Mapfuncmain(){r:=gin.Default()r.Use(Logger())//使用中间件r.GET("/index",func(c*gin.Context){Info("maingoroutine")//打印日志c.JSON(200,gin.H{"message":"index",})})r.Run()}funcLogger()gin.HandlerFunc{returnfunc(c*gin.Context){requestIdMap[goid.Get()]=uuid.New().String()//在日志中间件中为每个请求设定c.Next()}}funcInfo(msgstring){now:=time.Now()nowStr:=now.Format("2006-01-0215:04:05")fmt.Printf("%s[%s]%s\n",nowStr,requestIdMap[goid.Get()],msg)//打印日志}

这样的实现很简单,但是问题也很多。

第一个问题就是,在Go程序中,一次请求可能会涉及到多个goroutine,用这种方式很难在多个gotoutine之间传递RequestId。

在下面的代码中,如果新启动了一个goroutine,就会导致日志中获取不到RequestId:

funcmain(){r:=gin.Default()r.Use(Logger())r.GET("/index",func(c*gin.Context){Info("maingoroutine")gofunc(){//这里新启动了一个一个goroutineInfo("goroutine1")}()c.JSON(200,gin.H{"message":"index",})})r.Run()}

获取goroutineid也不是一种常规的做法,一般要通过hack的方式来获取,这种做法已经不推荐了。而且这个全局的map为了并发安全,在实际的使用中,可以还需要用到锁,在高并发的情况下必然会影响性能。

在每个请求结束的时候,还需要手动的把requestId从map中删除,否则就会造成内存泄漏。

总的来说,使用map这种方式来实现并不是很好。

在上面的代码中,我们使用一个hack的方式去获取goroutineid,这种方式早就不推荐使用,更推荐使用Context,关于Context内容,可以去看我之前的文章,在这里就不多说了。

在传递RequestId的场景中,同样也可以使用Context来实现,使用Context好处很明显,Context生命周期与请求相同,不需要手动销毁。而且Context是每个请求独享的,也不用担心并发安全的问题,Context还可以在goroutine之间传递。

使用Context实现的代码如下:

funcmain(){r:=gin.Default()r.Use(Logger())r.GET("/index",func(c*gin.Context){ctx,_:=c.Get("ctx")Info(ctx.(context.Context),"maingoroutine")gofunc(){Info(ctx.(context.Context),"goroutine1")}()c.JSON(200,gin.H{"message":"index",})})r.Run()}funcLogger()gin.HandlerFunc{returnfunc(c*gin.Context){valueCtx:=context.WithValue(c.Request.Context(),"RequestId",uuid.New().String())c.Set("ctx",valueCtx)c.Next()}}funcInfo(ctxcontext.Context,msgstring){now:=time.Now()nowStr:=now.Format("2006-01-0215:04:05")fmt.Printf("%s[%s]%s\n",nowStr,ctx.Value("RequestId"),msg)}

这样在一个请求中,所有的gotroutine都可以获取到同一个RequestId,而且不用担心内存泄漏和并发安全。

但是使用Context也有个问题就是需要每次传递Context,很多人还不习惯使用这种方式。其实Go官方早就推荐使用Context了,通常会把Context作为函数的第一个参数。如果函数使用结构体作为参数,也可以直接把Context作为结构体的一个字段

Context除了使用可以同来传递RequestId之外,还可以用来控制goroutine的生命周期,这些内容在之前的Context文章中详细说明了,感兴趣的可以去看看。

获取goroutineid这种方式应该被抛弃,而是应该使用Context,Go官方也早就推荐使用这种方式,在上文中,我们使用Context来传递RequestId,除此之外还可以用来传递单个请求范围的值,比如认证的token之类的,应该习惯在代码中使用Context。

到此这篇关于详解如何在Go服务中做链路追踪的文章就介绍到这了,更多相关服务中做链路追踪内容请搜索完美下载以前的文章或继续浏览下面的相关文章希望大家以后多多支持完美下载!


相关资料:txt下载doc下载文章搜索网址搜索百度搜索、好搜搜索、搜狗搜索

本文链接:http://www.gpxz.com/article/7f23f77ed1c32d681c4a.html


全局中部横幅
全局中部横幅
防务新观察

《防务新观察》是一档战略话题栏目,从最新的新闻事件出发,以新视角、新思路对“新安全观”进行详尽的阐述和分析,秉承“新闻演播室就是新闻现场”的现代传播理念,用新闻事件在国家防务层面的广度、深度为增强全民国防意识和国家安全防务理念服务。

28创业网

28创业网为创业者提供各类品牌全面的加盟项目,包括各大品牌的加盟费用、加盟条件、加盟电话、加盟流程、加盟选址等,为创业者提供更多有价值的参考信息,助力轻松创业!

明星频道首页

360明星是汇聚最全明星信息的综合站点,将内地、港台、欧美、日韩等全球明星八卦新闻、视频一网打尽,提供最全面完整的明星资料检索。

企业培训系统平台

【云学堂官网】是为中大型企业提供在线企业培训系统学习平台,有elearning企业学习平台搭建、企业培训课程、企业培训考试系统、绚星APP学习平台等;为企业提供专业的企业培训一站式解决方案。

下饭菜的做法大全

豆果美食下饭菜栏目为您推荐下饭菜做法大全,下饭菜怎么做好吃技巧分享,下饭菜最正宗的做法和下饭菜家常做法推荐,更多下饭菜的简单做法就来豆果美食。

太平洋汽车

太平洋汽车为您提供最专业,最全面的汽车报价、汽车图片信息,包括各类车型评测、报价、参数、配置、相关新闻和图片等,想了解更多汽车信息,就上太平洋汽车

数码

hao123网址导航数码频道,提供准确、丰富、全面的数码产品、I产品的报价、资讯、导购、行情等信息。

抖音林客|抖音生活服务服务商平台

抖音林客是为抖音生活服务的服务商打造的平台,服务商合作伙伴可在该平台为生活服务商家提供服务,为商家提供代运营服务和营销解决方案,助力商家长效经营。平台服务能力包括但不限于商家招募、商品管理、内容制作、店铺运营、商家培训、广告投流、产品能力等全方位服务内容。

方形不锈钢水箱厂家

不锈钢水箱【电话:13708856998】昆明市官渡区健卫不锈钢水箱厂专业生产健卫不锈钢水箱,不锈钢保温水箱,组合式不锈钢水箱,保温水箱,消防水箱,昆明健卫不锈钢水箱厂是云南地区创办最早,规模最大的不锈钢水箱生产商,健卫水箱兼产品销售及工程承包,保证最低的不锈钢水箱价格,最大限度的降低工程成本,满足广大客户的需求

专业PCB抄板公司提供电路板抄板

我公司是全国更好更专业的pcb抄板公司,主要提供pcb抄板、电路板抄板、手机板抄板、工控板抄板、医疗抄板等多层pcb抄板以及芯片解密、单片机解密、样机制作等一条龙反向服务。

全局底部横幅