您的位置:

分布式链路追踪

随着互联网业务的不断增长,单一服务越来越难以支撑整个业务的需求,因此出现了分布式系统架构。在分布式架构中,一次业务请求可能会涉及多个服务之间的调用,此时需要一种工具来协调和追踪整个请求的流转情况,即分布式链路追踪。

一、分布式链路追踪监控插件

分布式链路追踪监控插件是实现分布式链路追踪的一种方式。通过将监控插件添加到业务系统中,在请求的不同阶段打标记并上报到追踪系统中,就可以了解整个请求的调用链路,并通过可视化界面展示出来。

Java 示例:

public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        Tracer tracer = GlobalTracer.get();
        Span span = tracer.buildSpan("my-filter").start();
        Tags.COMPONENT.set(span, "my-servlet");
        try (Scope ignored = tracer.activateSpan(span)) {
            chain.doFilter(request, response);
        } finally {
            span.finish();
        }
    }

    @Override
    public void destroy() {
    }

}

Python 示例:

@app.route('/hello')
def hello():
    with tracer.start_active_span('hello') as scope:
        sid = uuid.uuid1().hex
        scope.span.set_tag('sid', sid)
        result = requests.get('http://localhost:8080/world')
    return 'hello, world!'

二、分布式统计和过滤的链路追踪

分布式统计和过滤的链路追踪除了具备监控插件的功能外,还可以对链路数据进行统计和过滤。通过在请求头和响应头中添加特定的字段,在追踪系统中可以对链路的运行时间、错误次数等信息进行统计,并设置告警策略。同时,也可以根据需求对链路数据进行筛选和聚合,以便更好地了解业务请求的情况。

Go 示例:

func Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        url, _ := url.Parse(r.URL.String())
        span, ctx := opentracing.StartSpanFromContext(r.Context(), "http")
        opentracing.Tag{Key: "http.method", Value: r.Method}.Set(span)
        opentracing.Tag{Key: "http.url", Value: url.Path}.Set(span)
        opentracing.Tag{Key: "http.query", Value: url.RawQuery}.Set(span)
        opentracing.Tag{Key: "http.host", Value: r.Host}.Set(span)
        opentracing.Tag{Key: "http.protocol", Value: r.Proto}.Set(span)
        defer span.Finish()
        w.Header().Add("Span-Id", span.Context().(SpanContext).TraceID().String())
        ctx = opentracing.ContextWithSpan(r.Context(), span)
        r = r.WithContext(ctx)
        next.ServeHTTP(w, r)
    })
}

三、分布式链路追踪传递上下文

在分布式架构中,一次请求可能会经过多个服务,需要将调用链路上的上下文信息传递下去,以方便追踪整个请求的流转情况。标准的传递方式是将上下文信息放在 HTTP 头中进行传递。具体实现方式可以使用各个编程语言提供的追踪库。

PHP 示例:

function curl($url) {
    $parentSpan = $tracer->getActiveSpan();
    $opts = [
        'http' => [
            'method' => 'GET',
            'header' => [
                'X-B3-TraceId: ' . $parentSpan->getContext()->getTraceId(),
                'X-B3-SpanId: ' . $parentSpan->getContext()->getSpanId(),
                'X-B3-ParentSpanId: ' . $parentSpan->getContext()->getParentId()
            ]
        ]
    ];
    $context = stream_context_create($opts);
    $response = file_get_contents($url, false, $context);
    return json_decode($response, true);
}

四、分布式链路追踪框架

分布式链路追踪框架是对分布式链路追踪进行封装的工具,旨在提供便捷的使用体验。一般来说,分布式链路追踪框架需要具备以下特性:

  • 支持多种追踪后端,比如 Zipkin、Jaeger;
  • 提供丰富的 API,方便集成;
  • 支持跨语言的追踪链路;
  • 支持链路信息灵活采样,减轻追踪系统压力。

Node.js 示例:

const Tracer = require('jaeger-client').Tracer;
const opentracing = require('opentracing');
const tracer = new Tracer({
    serviceName: 'hello-world',
    sampler: {
        type: 'const',
        param: 1,
    },
    reporter: {
        logSpans: true,
        agentHost: 'localhost',
        agentPort: 6832,
    }
});
opentracing.initGlobalTracer(tracer);

五、分布式链路追踪有哪些

目前常用的分布式链路追踪工具主要包括 Zipkin、Jaeger、SkyWalking 等。

  • Zipkin:国内外比较常用的分布式链路追踪工具,使用 Google 的 Dapper 作为参考,初版于 2012 年开发出来,已经发展得非常成熟;
  • Jaeger:Uber 开源的分布式链路追踪工具,支持多语言,特别是对 Go 语言的支持非常好;
  • SkyWalking:Apache 基金会孵化的 APM 工具,早期使用的是基于字节码增强的方式实现,后来也支持了插件的方式;

Zipkin 示例:

docker run -d -p 9411:9411 openzipkin/zipkin

Jaeger 示例:

docker run -d -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest

SkyWalking 示例:

docker run -d -p 8080:8080 apache/skywalking-oap-server

六、分布式链路追踪技术

分布式链路追踪涉及到的技术主要包括分布式系统和追踪系统两个方面:

  • 分布式系统:在分布式系统中,面临着多个服务之间互相调用的挑战。为了解决这个问题,需要使用一些分布式技术,比如分布式一致性算法、分布式事务等;
  • 追踪系统:对于追踪系统来说,需要对请求链路进行跟踪和统计。这就需要用到开发语言的追踪库以及一些可视化的追踪工具。

Java 示例:

@Bean
public Tracer jaegerTracer() {
    return new io.jaegertracing.Configuration(jaegerServiceName, samplerConfig, reporterConfig)
            .getTracer();
}

七、分布式链路追踪组件

除了常见的追踪工具外,还有一些组件可以集成在应用中,便于进行分布式链路追踪。

  • OpenTracing:CNCF 组织推出的跟踪标准,提供统一的 API 以及多种编程语言的实现方式;
  • Spring Cloud Sleuth:Spring Cloud 基于 OpenTracing 平台的分布式链路追踪框架,提供对 Zipkin 的集成;
  • Dapper:Google 开源的跟踪系统,后来成为 Zipkin 的灵感来源。

OpenTracing 示例:

Tracer tracer = Configuration.fromEnv().getTracer();
Span span = tracer.buildSpan("user-query").start();
span.setTag("user-id", "12345");
try {
    ...
} catch (Exception e) {
    span.setTag("error", true);
} finally {
    span.finish();
}

八、分布式链路追踪系统

分布式链路追踪系统是对分布式链路追踪进行统一管理的工具,一般包括追踪器、存储器、查询器等模块。通过将分布式链路追踪数据集中存储,方便进行查看和分析。

Zipkin 示例:

docker run -d -p 9411:9411 openzipkin/zipkin

九、分布式链路追踪工具

分布式链路追踪工具是对分布式链路追踪进行可视化展示和分析的工具,可以使用界面来查看链路的调用情况,方便问题排查和性能优化。

Zipkin 示例:

docker run -d -p 9411:9411 openzipkin/zipkin

Jaeger 示例:

docker run -d -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest

十、分布式链路追踪实现选取

在选取分布式链路追踪实现时,需要根据实际需求来进行选择。一般需要考虑以下几个方面:

  • 语言支持:判断追踪框架是否支持应用所使用的语言;
  • 性能:对追踪框架性能进行评估,看是否对业务系统带来过多负担;
  • 社区活跃度:优先选择社区活跃、开发维护频繁的追踪框架;
  • 集成便捷性:判断追踪框架是否提供了丰富的