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

全局中部横幅
搜索蜘蛛

通过本工具可以快速模拟搜索引擎蜘蛛访问页面所抓取到的内容信息,可以判断网站有没有被做跳转,否则影响SEO优化。

Chrome插件(谷歌浏览器插件)

Chrome插件又称为谷歌浏览器插件,是谷歌Chrome浏览器的扩展插件,使用Chrome插件可以为Chrome浏览器带来一些功能性的扩展,进而提高Chrome的使用体验。想要获得Chrome插件的方式有许多,用户可以直接在Chrome商店中下载和安装谷歌浏览器插件,也可以通过huajiakeji.com来获得更加详细的介绍和优秀Chrome插件的推荐与下载服务。

零担物流

笨熊物流是熊居网旗下针对厂区与物流园区货运物流提供高效、智能、便捷的物流服务平台,为厂区货主找车发货、货车司机找活送货,提供全面的信息及交易服务,致力打造互联网+时代下的新型智能物流服务模式

哈尔滨奢侈品回收

酬诚奢侈品回收公司是专业提供哈尔滨奢侈品回收、黑龙江名包回收、二手奢侈品回收、名表回收、黄金回收、名牌鞋服回收、钻戒回收等的商业服务公司。电话:18045104444公司坐落于黑龙江省哈尔滨市。

耐腐蚀化工泵,管道排污泵,多级管道泵,不锈钢离心泵,自吸磁力泵

浙江扬子江泵业有限公司是专业从事各类离心泵,磁力泵,排污泵,真空泵,自吸泵,隔膜泵,化工泵,齿轮泵,螺杆泵,浓浆泵,泥浆泵,多级泵,旋涡泵,液下泵,污水泵,潜水泵潜水排污泵,气动隔膜泵,耐腐蚀泵,自吸排污泵,耐腐蚀泵,油泵,变频无负压恒压供水设备产品生产和销售的企业,公司以良好的售后服务和优质的解决方案,欢迎来电洽谈等。

单梁起重机

河南省聚力德机电设备有限公司拥有多年行业经验生产商,主营电动单梁起重机,双梁桥式起重机,花架龙门起重机,导轨式货梯,钢丝绳电动葫芦,单双梁起重机零配件等产品,开拓起重设备市场,为客户提供起重解决方案。

PPT模板

163办公是一家专注办公素材设计与制作创意模板下载平台,拥有海量优质的PPT模板、Word模板、Excel模板、PDF模板,可编辑替换,即下即用,提升您的工作效率!

2020河南民权制冷装备博览会

2020河南·民权第六届制冷装备博览会民权制冷展2020-10-28开幕2020河南民权制冷装备博览会欢迎您4006-098-298

【集发布网】免费发布信息平台,免费发布供求信息的网站,同城分类百姓网,网络推广平台大全,全国免费发布信息网

集发布网,免费发布信息平台_同城分类百姓网_免费发布供求信息的网站,这里有各行业信息网/B2B电子商务网站,是最方便的免费网络推广平台,已成为无数商家网络营销/网络推广的首选网站!网络推广平台大全_全国免费发布信息网

【TITAIKE】T太科网

TITAIKE是国内一家整合及翻译国内外基岩版地图的网站,在整合优质国外基岩版地图的前提下,我也接收来自国内各大工作室及个人的作品。“Minecraft”以及“我的世界”为MojangSynergiesAB的商标,本站与网易公司、Mojang以及微软公司没有从属关系

全局底部横幅