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


本文地址: https://www.gpxz.com/article/7f23f77ed1c32d681c4a.html
全局中部横幅
全局中部横幅
pm2.5

大气110是依托国家环保污染源监控工程技术中心、罗克佳华研发的集空气质量监测、pm2.5指数、雾霾指数等功能于一体的实时查询服务平台,真实评估空气污染的健康威胁与影响,为公众提供身边环境状况服务和健康生活向导,为专业机构防护治理空气污染提供专业一体化服务。

评论

评论频道是环球网的特色原创频道。环球网评论频道长期坚持“理性解读中国客观解读世界”的理念,逐渐形成了高端、权威、全面、争鸣等特点,严谨而不失活泼、犀利而不乏理性,在纷繁复杂的互联网上有效展示环球人的声音。

氯化镧

山东德盛新材料有限公司立足稀土研发,目前已拥有稀土氧化物、稀土硝酸盐、稀土氯化盐、稀土硫酸盐、稀土醋酸盐、稀土碳酸盐、稀土氟化物、稀土草酸盐、氢氧化物等九大系列100多种稀土系列产品,产品广泛应用于催化剂、石油、化工、电子、医药、陶瓷、金属合金、磁性材料等行业。

七速云

七速云是国内优秀的企业级云计算服务提供商。专注云技术研发,主要面向广大开发者、政企用户、金融机构等,提供基于智能云服务器的全方位云计算解决方案,为用户提供可信赖的企业级公有云服务。

有鸿微校园

有鸿微校园-智慧校园解决方案服务商

喷砂机

吉川科技是一家喷砂机设计生产厂家,提供喷砂机、手动喷砂机、自动喷砂机、液体喷砂机、喷砂房、喷砂设备、杭州喷砂机、喷砂机厂家、湿式喷砂机、喷砂机价格、喷丸机、苏州喷砂机、数控喷丸机。

亿个村

亿个村是关于乡村的网站,在这里可以找到一个村的介绍,相关图片,村里过去与最近发生的事

重庆飘逸数字科技有限公司

重庆飘逸数字科技有限公司是专注于现代农业技术服务的专业平台,提供智慧农业整体解决方案、精准种植技术指导和农产品供应链优化服务。我们整合农业物联网、智能灌溉和大数据分析技术,帮助农户实现科学种植、降本增效,推动农业产业数字化升级,为现代农业发展提供全方位技术支持。

东莞澳新软件科技有限公司

东莞市澳新软件科技有限公司(以下简称澳新)成立于2011年,专业从事钢材、模具材料、模架、五金配件、玩具等相关行业现代化ERP,App等管理系统及互联网行业方案的提供商和服务商,拥有一支学历高、知识面广,涵盖各行各业均具有10年以上信息化管理经验的优秀团队。主要业务:行业ERP系统、电子商务、APP、微信定制开发、POS系统、模具钢ERP、钢材ERP、模架ERP

手机软件下载

河东手机站为用户提供最新海量安卓软件、安卓游戏、苹果软件、苹果游戏下载,并且每日更新海量手机软件与相关app操作教程!

寰球国际展览(GIE)

寰球国际展览,广东寰球国际会展有限公司、GIE、议题策划、专家会议、高层峰会、学术会议、新闻发布、行业会议、商业活动、巡演活动、赛事活动、推广活动、媒介活动、年活动、颁奖活动、奠基仪式、签约仪式、启动仪式、开业仪式、展览会议、展会搭建、新能源汽车、智能汽车、无人驾驶汽车、自动驾驶、5G、人工智能、无人驾驶大会、智能驾驶大会

无锡市新达机电成套设备厂

我厂是专门从事各种管状电热元件的开发、设计、制造、销售服务的一体化企业

全局底部横幅