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

全局中部横幅
首页

PLM是一种应用于在单一地点的企业内部、分散在多个地点的企业内部,以及在产品研发领域具有协作关系的企业之间的,支持产品全生命周期的信息的创建、管理、分发和应用的一系列应用解决方案,它能够集成与产品相关的人力资源、流程、应用系统和信息

凉菜的家常做法

凉菜的做法_家常凉菜的做法非常简单易学。豆果美食提供的图文凉菜的家常做法大全和凉菜的视频,用最短的时间让你学会凉菜的做法

上海电商云仓

上海飞进物流有限公司24小时服务热线021-69925759!专业为您提供上海电商云仓,一件代发,电商仓储托管,化妆品贴标,电商代发等业务,及先进的运输工具和精英管理团队,能够为您提供最专业的仓储配送物流解决方案.

电影

视频服务平台,提供视频播放,视频发布,视频搜索,视频分享

河南成特教育科技有限公司,郑州技校,河南技校,技术学院,职业学校

河南成特教育科技有限公司经政府部门批准的以特种作业人员安全技术培训和考核为一体专业公司,主要培训专业:挖掘机,叉车,电工,焊工等专业。

山东天狐信息科技有限公司官方网站,公司坚持以技术创新为主导,自主研发天狐系统(天狐CMS

互联网行业瞬息万变,谁能洞察未来,谁就能引领行业发展。先进的技术+远瞻的目光,天狐做行业内最受人尊敬的专业网络营销服务提供商!互联网将世界变小,我们帮您的公司做大!山东天狐全国招商热线:400-667-0536.

喜洗车官方网站

[v1.0]支持合肥400余家洗车行,各种类型乘用车一券通用,让洗车畅通无阻。低价洗车券、多类型特惠券、第三方推送免费洗车券~~~体验丰富多彩,真正低至零元。免费洗车红包、朋友圈分享送出红包,为企业提供广告投放跨界整合方案,直击有效人群,精准营销。

成都UV喷绘,5米喷绘,UV喷印,UV平板喷绘,UV喷绘价格,UV喷绘制作厂家

四川零贰捌广告有限公司提供UV喷绘,5米UV喷绘、UV平板喷绘、UV卷材喷绘、亚克力UV喷印、UV软膜喷绘、uv喷印等服务,产品主要适用于广告展示,装饰装修。公司配备专业配送人员与施工队伍,可提供各类安装施工服务

江苏科熔铁路器材有限公司

江苏科熔铁路器材有限公司是集电气化铁路器材、工程电气、制造、销售及服务于一体的企业。专业从事各种电缆桥架、电缆支架、隧道支架、母线槽、各类配电箱、高低压开关柜、铁路器材接触网零部件的生产及销售。

全局底部横幅