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

全局中部横幅
一站式PCBA加工厂家【深圳羚岳科技】

深圳市羚岳科技有限公司成立于2013年,专业为国内A股上市公司、创业板上市公司、国外集团公司等27个国家地区客户提供一站式EMS电子制造服务,包含PCBLayout、电子方案设计、PCB电路板制作、SMT贴片加工、DIP插件、PCBA功能测试、三防漆喷涂、成品组装、包装物流、清关等EMS服务

四川红网

四川红网是一个生活资讯平台。在这里有各种生活经验交流,知识分享,可以为您的智慧生活提供相应参考!

高效无声破碎剂

东科建材生产高效无声膨胀剂,70-90大孔径(潜孔钻)静态破碎剂,岩石爆破剂,无声炸药价格,破裂剂,静爆剂,混凝土破碎剂,养护剂,水性脱模剂,脱模油,模板漆.广泛用于矿山开采,石头静力破碎,混凝土拆除!

科智乐园

科智乐园网站为您打造一个充满惊喜与发现的数码科技世界,涵盖丰富的数码硬件产品、强大的科技软件、先进的智能系统以及持续的创新研发成果,带您领略科技的无限魅力。

河南工程监理

中豫建设工程咨询有限公司是专业的河南工程监理公司,提供工程项目管理、高速公路监理、致力于建设工程全生命周期内的策划咨询、前期可研、工程设计、招标代理、造价咨询、工程监理、施工前期准备、施工过程管理、竣工验收及运营保修等各个阶段的工程检测服务。

高考资讯

考神网(ks01.cn),是致力为广大考生和家长提供一个当年和历年高考分数线查询、高考政策动态、全国大学查询、高考志愿填报、历年高考分数线、高考常识、大学生活、高考历年真题、高考作文、考生心理辅导等频道的平台,旨在让各位考生和家长能够获得最有用的信息。

工业级氯化锌,电池级氯化锌生产厂家,价格

晋州市冀田锌业有限公司是开发研究生产电池级氯化锌、工业级氯化锌的专业氯化锌生产厂家,公司产品有年产15000吨的氯化锌、5000吨氧化锌、2000吨硫酸锌的规模,氯化锌价格让采购省10%

轴流泵

盐城市海洋水泵有限公司主营轴流泵,混流泵,立式轴流泵,成立于2001年,现拥有员工40余人,包括管理人员10人,技术人员5人。

便携式手术床,野战妇科手术床,便携式病床,便携式洗手器,成品输液智能分拣系统

中星众科医疗科技有限公司是一家集产品研发、生产和销售的医疗器械制造商,致力于为客户提供优质医疗产品和医院整体解决方案。公司秉承着研发创新的宗旨,通过人才引进组建了一批技术研发团队,在医用野战妇科手术床、便携式手术床、便携式病床、医用护理床、便携式洗手器、成品输液智能分拣系统及医疗设备等多个产品项目中实现技术突破.

西安合金管

西安鸿蒙实业有限公司13700230365现货销售:西安合金管,西安锅炉管,西安无缝管,西安厚壁管,西安大口径无缝管产品,选购管材产品请认准鸿蒙实业

重庆混凝土生产公司

重庆富普新材料科技股份有限公司(www.fupu.cn)是一家专注预拌普通及特种混凝土、C30混凝土、装配式建筑部品部件及混凝土预制构件、蒸压加气混凝土板及砌块、建筑用砂浆、水泥、透水水泥混凝土、ALC条板、ALC板材、外加剂等产业链延伸产品的厂家,集研发、生产、批发销售为一体的综合性绿色建材制造商。

全局底部横幅