工作用Go: 异步任务怎么写4 | Trace: 异步任务还能进行链路追踪么?

简介: 工作用Go: 异步任务怎么写4 | Trace: 异步任务还能进行链路追踪么?

go01.jpeg

本文为 工作用Go: 异步任务怎么写 系列的第4


随着可观测技术的不断演进, 基建上的不断提升, 链路追踪技术也进行了演进

  • trace1.0: opentracing jaeger 等
  • trace2.0: otel


当用户请求进来时, 可以通过 traceId 串联起用户的完成调用链, 监控和排查问题能力大大增强!

{
"code": 200,
"status": 200,
"msg": "成功",
"errors": null,
"data": "env-t0",
"timestamp": 1668696256,
"traceId": "0a340332637648c0009b4ca2a51bbb85"}


trace 通过请求(request)中的 context, 不断向下传递, 从而将当前请求的所用调用通过同一个 traceId 串联起来


funcTestTrace(t*testing.T) {
Op1(ctx) // 比如操作了 DBOp2(ctx) // 比如操作了 cacheTask(ctx)
log.Print("req done")
}
funcTask(ctxcontext.Context) {
// 使用自定义span, 将当前操作上报到trace_, span :=otel.GetTracerProvider().Tracer("task").Start(ctx, "xxxTask")
deferspan.End()
// 模拟耗时任务time.Sleep(time.Second)
log.Print("task done")
}


如同上面演示的 demo code:

  • 通过 ctx, 将当前请求(request)的所有操作使用同一个 traceId 串起来
  • otel 默认了很多操作的 trace 上报, 比如 mysql/redis/kafka 等等, 也可以使用自定义 span 的方式进行新增


如果要进行耗时任务异步处理, 直觉上直接 go 一下:

funcTestTrace(t*testing.T) {
Op1(ctx) // 比如操作了 DBOp2(ctx) // 比如操作了 cachegoTask(ctx)
log.Print("req done")
}


这时候脑海中陡然蹦出一个声音: 野生Goroutine

funcTestTrace(t*testing.T) {
Op1(ctx) // 比如操作了 DBOp2(ctx) // 比如操作了 cachegox.RunCtx(ctx, Task) // 在 gox.Run 的基础上, 添加 ctx 支持log.Print("req done")
}


可是等测试一下, 就会发现, task() 并没有执行!

细心的小伙伴就会发现, 这和开始的例子有点像呀, 而且对比下就会知道, 此处多了一个 ctx:

funcTestTask(t*testing.T) {
gotask(ctx)
log.Print("req done")
}
functask() {
// 模拟耗时任务time.Sleep(time.Second)
log.Print("task done")
}


  • 没有 ctx 的时候, 因为主协程一直在, 子协程可以处理完任务后退出, 也就是子协程的生命周期都在主协程内
  • 有 ctx 的时候, 由于 ctx 的存在, 请求(request)中主协程需要接受 ctx 控制, 异步处理后, 请求也就结束了(上面log.Print("req done")模拟的部分), 这是 ctx 就会控制子协程一起结束掉, 也就是子协程的生命周期都在当前请求的协程内


于是, 又有了 2 种处理办法:

  • 简单做法, 就像上面一样, 没有 ctx, 就没有问题了嘛. 如果用一句话来概括这种方法: 面试官: 你可以回家等消息了
  • 既然又要执行异步任务, 又要有 trace, 那把 trace 继续传下去, 用一个新的 ctx 就好了嘛


上代码:

  • 复制 ctx, 把 trace 继续传下去
packagectxkit// Clone 复制 ctx 中对应 key 的值,移除父级 cancel。funcClone(preCtxcontext.Context, keys...interface{}) context.Context {
newCtx :=context.Background()
// 从 pctx 开启一个子 span,来传递 traceId_, ospan :=otel.GetTracerProvider().Tracer(trace_in.InstrumentationPrefix+"/ctxkit").Start(preCtx, "ctxkit.Clone", otel_trace.WithAttributes(
trace_attr.AttrAsyncFlag.Int(1), // 标记为异步  ))
deferospan.End()
newCtx=trace.ContextWithSpan(newCtx, ospan)
returnctxClone(newCtx, preCtx, keys...)
}
// CloneWithoutSpan  功能同 Clone,但不会创建 trace span,建议在大批数据 for 循环之前使用,避免 span 链路过长。funcCloneWithoutSpan(preCtxcontext.Context, keys...interface{}) context.Context {
tid :=trace_in.GetOtelTraceId(preCtx)
iftid=="" {
tid=trace_in.FakeTraceId()
 }
newCtx :=context.WithValue(context.Background(), ictx.CtxKeyFakeTraceId, tid)
returnctxClone(newCtx, preCtx, keys...)
}
funcctxClone(baseCtx, preCtxcontext.Context, keys...interface{}) context.Context {
for_, key :=range_ctxKeys {
ifv :=preCtx.Value(key); v!=nil {
baseCtx=context.WithValue(baseCtx, key, v)
  }
 }
keys=append(keys, _strKeys...)
for_, key :=rangekeys {
ifv :=preCtx.Value(key); v!=nil {
baseCtx=context.WithValue(baseCtx, key, v) //nolint  }
 }
returnbaseCtx}


  • 实际使用
funcTestTask(t*testing.T) {
nexCtx :=ctxkit.Clone(ctx)
gotask(newCtx)
log.Print("req done")
}
functask() {
// 模拟耗时任务time.Sleep(time.Second)
log.Print("task done")
}


相关实践学习
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
目录
相关文章
|
6天前
|
Go 开发者
Go语言带缓冲通道:异步通信的艺术
Go语言带缓冲通道:异步通信的艺术
27 0
|
6天前
|
NoSQL Go Redis
Go异步任务处理解决方案:Asynq
Go异步任务处理解决方案:Asynq
207 1
Go异步任务处理解决方案:Asynq
|
7月前
|
中间件 Go API
Go语言微服务框架 - 9.分布式链路追踪-OpenTracing的初步引入
我们从API层到数据库层的链路已经打通,简单的CRUD功能已经可以快速实现。 随着模块的增加,我们会越发感受到系统的复杂性,开始关注系统的可维护性。这时,有个名词会进入我们的视野:**分布式链路追踪**
65 0
|
消息中间件 运维 Kubernetes
工作中用Go: Go中异步任务怎么写
工作中用Go: Go中异步任务怎么写
1915 0
工作中用Go: Go中异步任务怎么写
|
10月前
|
监控 NoSQL 数据可视化
一文带您了解Go异步任务处理解决方案:Asynq
一文带您了解Go异步任务处理解决方案:Asynq
377 0
|
10月前
|
Prometheus 监控 Cloud Native
Go微服务架构实战 下篇:1. gRPC + Opentracing + Zipkin实现分布式链路追踪系统
Go微服务架构实战 下篇:1. gRPC + Opentracing + Zipkin实现分布式链路追踪系统
|
安全 Go API
MoE 系列(四)|Go 扩展的异步模式
上一篇我们体验了用 Istio 做控制面,给 Go 扩展推送配置,这次我们来体验一下,在 Go 扩展的异步模式下,对 Goroutine 等全部 Go 特性的支持。
|
存储 Prometheus 监控
(16)go-micro微服务jaeger链路追踪
(16)go-micro微服务jaeger链路追踪
181 0
|
Java API Spring
异步线程中链路追踪方案
异步线程中链路追踪方案
710 0
|
Go 调度 开发者
并发与并行,同步和异步,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang并发编程之GoroutineEP13
如果说Go lang是静态语言中的皇冠,那么,Goroutine就是并发编程方式中的钻石。Goroutine是Go语言设计体系中最核心的精华,它非常轻量,一个 Goroutine 只占几 KB,并且这几 KB 就足够 Goroutine 运行完,这就能在有限的内存空间内支持大量 Goroutine协程任务,方寸之间,运筹帷幄,用极少的成本获取最高的效率,支持了更多的并发,毫无疑问,Goroutine是比Python的协程原理事件循环更高级的并发异步编程方式。
并发与并行,同步和异步,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang并发编程之GoroutineEP13