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


本文地址: /article/7f23f77ed1c32d681c4a.html
全局中部横幅
全局中部横幅
淘宝下载2024安卓最新版_手机app官方版免费安装下载_豌豆荚

【豌豆荚】为你提供淘宝APP安卓版下载,历史版本、旧版下载,查看2024最新淘宝手机版介绍、应用截图、网友评论,方便快捷的将安卓版淘宝应用免费下载到手机。

新乡市鹏锦特种电机有限公司

新乡市鹏锦特种电机有限公司是生产各类特种电机生产,销售、研发为一体的综合性企业,公司主营:变频振动电机、箱式激振器、变频辊道电机、辊道用变频调速电机、力矩三相异步电动机等产品多型号齐全,支持定制加工,欢迎来电洽谈。

智能装备-山东鸿威智能装备有限公司

合易百科网每天更新各类百科知识问答,合易百科提供精准知识问答,行业知识问答,各种百科小知识解答,以及生活常识问答。学知识,来合易百科网!

莱赞_Lazada店群软件官方网站

莱赞店群管理系统,莱赞店铺管理软件,莱赞店铺管理软件哪个好,莱赞店铺管理软件有哪些

手机游戏下载-安卓手机软件下载-2025手游排行榜 - 12365手游网

12365手游网为您提供热门手机游戏下载,绿色安全的安卓手机软件下载,每天持续更新手机新游戏、应用资源安装包,当下新出人气游戏软件会以人气手游排行榜榜单数据形式推荐给大家,找好游玩新游看攻略就来www.12365.sd.cn。

农业种植网 - 种植技术综合信息平台

农业种植网,种植领域专业网站,为广大农民朋友提供水果、蔬菜、中草药、农机和园林绿化等行业信息。

航空货运-机场快递运输-空运加急-航空物流官网

【机场货运部】航空物流快递,空运加急当日达,航空运输服务站-业务包括:机场物流,航空快运加急,航空当天件等机场航空货物运输的快速发货;根据客户的需求提供“一站式”航空物流解决方案,为实现客户价值最大化,提供贴心到位的专业服务。航空货物运输空运网络遍及国内各地通航机场.时效可做到当天达;限时6-8小时到港自提;12-24小时送货上门的货运服务;同时更为客户推出个性化、多元化的贴心服务,咨询、下单、上门取货等二十四小时客服全程一对一实时响应。

燎元跃动|www.cheapviagraws.com

燎元跃动(www.cheapviagraws.com)分析互联网生活热门、热点知识,热门软件,收藏本站成为一名百科达人。

锐驰物联-全球领先的RFID标签&无线传感产品解决方案商

锐驰物联是集研发、生产、销售为一体的高新科技企业,专注为智能零售、智能资产、智慧医疗、智慧洗涤及工业物联网等领域提供无源的RFID标签研发设计及生产制造。

全局底部横幅