分布式链路追踪续集

k8s技术圈

共 6121字,需浏览 13分钟

 · 2021-10-09

本文是 分布式链路追踪 的续集。

在上一文中提到为了统一各种分布式追踪系统的实现,CNCF (云原生计算基金会)下的 OpenTracing 项目定义了一套分布式追踪的标准,可以使用 OpenTracing API 完成代码的监控埋点,最后用户自由选择遵循 OpenTracing 标准的链路追踪系统,比如 jaeger 。

但是现在访问 OpenTracing 的 官网[1] ,可以发现官网提醒 OpenTracing 和 OpenCensus 已经被合并成为 OpenTelemetry 。(在上文编写时我未发现到,感谢 Donald Liu 大佬的提醒)

cncf[2] 上也可以发现 OpenTracing 已经被 Archived 了

相应地,OpenTelemetry 已经正式成为了 CNCF 的孵化项目 参考[3]

Both OpenTracing and OpenCensus will be further deprecated in the coming weeks, with OpenTracing being formally archived by the CNCF TOC.

缘由

参考[4]

在 APM (Application Performance Monitoring) 领域,或者说微服务的可观察性方面包括有分布式链路追踪,指标监控和日志。

OpenTracing 是最早为分布式追踪制定了一套平台无关、厂商无关的协议标准的项目,并以此成为了 CNCF 的孵化项目。

在之后,谷歌牵头,微软加入,创建了 OpenCensus 项目统一 Metrics 基础指标监控的使用方式,还做了 OpenTracing 的老本行:分布式追踪。

一山不容二虎,OpenTracing 和 OpenCensus 愈打愈烈,对我们用户来讲,实在是太不友好了。

然后 OpenTelemetry 横空出世了,OpenTracing 和 OpenCensus 既然都这么好,干脆你们合并起来吧,我 OpenTelemetry 来兼容你们。

OpenTelemetry 的自身定位十分明确:数据采集和标准规范的统一,对于数据如何去使用、存储、展示、告警,官方是不涉及的。

OpenTelemetry 的终极目标十分伟大:实现 Metrics、Tracing、Logging 的融合及大一统,作为 APM 的数据采集终极解决方案。

然后就是现在的故事了,OpenTelemetry 正式成为 CNCF 的孵化项目,OpenTracing 和 OpenCensus 不再维护。

兼容性

这个放心,大部分的知识点定义还是一致的 (如数据模型:Trace, Span, SpanContext) ,但是 API 的使用会有所区别,详细可以查看 OpenTelemetry 规范文档[5]

承接上文

我们现在对上文的 OpenTracing API 的代码做出修改,拥抱 OpenTelemetry 吧!

安装 OpenTelemetry for Go 依赖

go get go.opentelemetry.io/otel@v1.0.0-RC1 go.opentelemetry.io/otel/sdk@v1.0.0-RC1 go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC1 go.opentelemetry.io/otel/trace@v1.0.0-RC1

安装 OpenTelemetry for jaeger 依赖

go get -u go.opentelemetry.io/otel/exporters/jaeger

OpenTelemetry 官方为多种开源框架提供了开箱即用的 Instrumentation Packages ,如 gin , beego , mux , go-kit 等,当然也支持 net/http 标准库 ,更多可浏览opentelemetry-go-contrib[6]

我们使用的是 net/http 标准库,所以直接导入 otelhttp

go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp

main.go 修改如下:

package main

import (
 "fmt"
 "log"
 "math/rand"
 "net/http"
 "time"

 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
 "go.opentelemetry.io/otel"
 "go.opentelemetry.io/otel/attribute"
 "go.opentelemetry.io/otel/exporters/jaeger"
 "go.opentelemetry.io/otel/propagation"
 "go.opentelemetry.io/otel/sdk/resource"
 tracesdk "go.opentelemetry.io/otel/sdk/trace"
 semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
 "go.opentelemetry.io/otel/trace"
)

func init() {
 exp, err := jaeger.New(jaeger.WithAgentEndpoint())
 if err != nil {
  panic(err)
 }
 tp := tracesdk.NewTracerProvider(
  tracesdk.WithSampler(tracesdk.AlwaysSample()),
  tracesdk.WithBatcher(exp),
  tracesdk.WithResource(resource.NewWithAttributes(
   semconv.SchemaURL,
   semconv.ServiceNameKey.String("opentelemetry-example"), // 服务名
   semconv.ServiceVersionKey.String("0.0.1"),
  )),
 )
 otel.SetTracerProvider(tp)
 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
}

func main() {
 port := 8080
 addr := fmt.Sprintf(":%d", port)
 mux := http.NewServeMux()
 mux.HandleFunc("/", indexHandler)
 mux.Handle("/home", otelhttp.NewHandler(http.HandlerFunc(homeHandler), "请求 /home"))
 mux.Handle("/async", otelhttp.NewHandler(http.HandlerFunc(serviceHandler), "请求 /async"))
 mux.Handle("/service", otelhttp.NewHandler(http.HandlerFunc(serviceHandler), "请求 /service"))
 mux.Handle("/db", otelhttp.NewHandler(http.HandlerFunc(dbHandler), "请求 /db"))
 fmt.Printf("http://localhost:%d\n", port)
 log.Fatal(http.ListenAndServe(addr, mux))
}

// 主页 Html
func indexHandler(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte(` 点击开始发起请求 `))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("开始请求...\n"))

 ctx := r.Context()
 span := trace.SpanFromContext(ctx)
 defer span.End()

 // 发起异步请求
 asyncReq, _ := http.NewRequest("GET""http://localhost:8080/async"nil)
 // 传递span的上下文信息
 // 将关于本地追踪调用的span context,设置到http header上,并传递出去
 otel.GetTextMapPropagator().Inject(ctx,
  propagation.HeaderCarrier(asyncReq.Header),
 )
 go func() {
  if _, err := http.DefaultClient.Do(asyncReq); err != nil {
   span.RecordError(err)
   span.SetAttributes(
    attribute.String("请求 /async error", err.Error()),
   )
  }
 }()

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)

 // 发起同步请求
 syncReq, _ := http.NewRequest("GET""http://localhost:8080/service"nil)
 otel.GetTextMapPropagator().Inject(ctx,
  propagation.HeaderCarrier(syncReq.Header),
 )
 if _, err := http.DefaultClient.Do(syncReq); err != nil {
  span.RecordError(err)
  span.SetAttributes(
   attribute.String("请求 /service error", err.Error()),
  )
 }
 w.Write([]byte("请求结束!"))
}

// 模拟业务请求
func serviceHandler(w http.ResponseWriter, r *http.Request) {
 // 通过http header,提取span元数据信息
 span := trace.SpanFromContext(
  otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),
 )
 defer span.End()

 dbReq, _ := http.NewRequest("GET""http://localhost:8080/db"nil)
 otel.GetTextMapPropagator().Inject(r.Context(), propagation.HeaderCarrier(dbReq.Header))
 if _, err := http.DefaultClient.Do(dbReq); err != nil {
  span.RecordError(err)
  span.SetAttributes(
   attribute.String("请求 /db error", err.Error()),
  )
 }

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
}

// 模拟DB调用
func dbHandler(w http.ResponseWriter, r *http.Request) {
 // 通过http header,提取span元数据信息
 span := trace.SpanFromContext(
  otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),
 )
 defer span.End()

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
}

同样的部署到集群中

kubectl apply -f https://raw.githubusercontent.com/togettoyou/jaeger-example/master/jaeger-example-opentelemetry.yaml -n observability

访问后观察 Jaeger UI 可以发现调用链和之前的也一致,包含 5 个 Span

具体 Span 信息

参考资料

[1]

官网: https://opentracing.io/

[2]

cncf: https://www.cncf.io/archived-projects/

[3]

参考: https://www.cncf.io/blog/2021/08/26/opentelemetry-becomes-a-cncf-incubating-project/

[4]

参考: https://github.com/open-telemetry/docs-cn/blob/main/OT.md

[5]

OpenTelemetry 规范文档: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md

[6]

opentelemetry-go-contrib: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/README.md


浏览 55
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报