看完这篇文章我奶奶都懂Opentracing了 (三)

三. 扩展点分析

通过前面的分析我们了解到,Opentracing对分布式链路追踪中的各种概念进行了统一的定义,某种程度上,已经成为分布式链路追踪的规范。

Java语言中,Opentracing定义了诸如SpanTracer等概念对应的接口,不同的分布式链路实现方需要结合具体的实现方案来提供相应实现,例如本文选择的Jaeger,其提供的JaegerSpan实现了Span接口,JaegerTracer实现了Tracer接口等。

现在接口定义已经有了,具体的实现也有了,该怎么用起来呢。在本文的示例中,具体的使用案例就是我们提供的RestTemplate拦截器,以及过滤器TracingFilter,那么问题就来了,为什么我知道可以这么用,是因为我比较聪明吗,那必然不是,当然是Opentracing告诉我该这么用,所以我才这么用,既然Opentracing定义好了接口,还告诉了用户该怎么用,那么有没有一种可能,Opentracing来提供RestTemplate拦截器,来提供过滤器TracingFilter呢,那完全是有可能的,Opentracing也正是这么做的。

OpentracingRestTemplate提供了一个拦截器叫做TracingRestTemplateInterceptor,也提供了一个过滤器叫做TracingFilter,好吧,到这里我就不装了,示例中的RestTemplate拦截器和过滤器TracingFilter,其实就是抄的Opentracing的,不过我没抄全,毕竟我只是需要搭建一个演示demo,所以官方的很多为了提升扩展性的扩展点,我都给砍掉了,而这些扩展点,正是我们基于已有的轮子造更好的轮子的基础,也正是本节的分析重点。

1. ServletFilterSpanDecorator和RestTemplateSpanDecorator

我们先看一下io.opentracing.contrib.web.servlet.filter.TracingFilter中有哪些扩展点。OpentracingServlet提供了一个专门服务于分布式链路追踪的过滤器TracingFilter,其实现了javax.servlet.Filter接口,关键的doFilter() 方法如下所示。

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
        throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
    HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;


    if (!isTraced(httpRequest, httpResponse)) {
        chain.doFilter(httpRequest, httpResponse);
        return;
    }

    if (servletRequest.getAttribute(SERVER_SPAN_CONTEXT) != null) {
        chain.doFilter(servletRequest, servletResponse);
    } else {

        SpanContext extractedContext = tracer.extract(Format.Builtin.HTTP_HEADERS,
                new HttpServletRequestExtractAdapter(httpRequest));


        final Span span = tracer.buildSpan(httpRequest.getMethod())
                .asChildOf(extractedContext)
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
                .start();

        httpRequest.setAttribute(SERVER_SPAN_CONTEXT, span.context());



        for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
            spanDecorator.onRequest(httpRequest, span);
        }


        try (Scope scope = tracer.activateSpan(span)) {
            chain.doFilter(servletRequest, servletResponse);
            if (!httpRequest.isAsyncStarted()) {
                for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
                    spanDecorator.onResponse(httpRequest, httpResponse, span);
                }
            }
        } catch (Throwable ex) {

            for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
                spanDecorator.onError(httpRequest, httpResponse, ex, span);
            }
            throw ex;
        } finally {
            if (httpRequest.isAsyncStarted()) {

                httpRequest.getAsyncContext()
                        .addListener(new AsyncListener() {
                            @Override
                            public void onComplete(AsyncEvent event) throws IOException {
                                HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
                                HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();

                                for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
                                    spanDecorator.onResponse(httpRequest,
                                            httpResponse,
                                            span);
                                }
                                span.finish();
                            }

                            @Override
                            public void onTimeout(AsyncEvent event) throws IOException {
                                HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
                                HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();

                                for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
                                    spanDecorator.onTimeout(httpRequest,
                                            httpResponse,
                                            event.getAsyncContext().getTimeout(),
                                            span);
                                }
                            }

                            @Override
                            public void onError(AsyncEvent event) throws IOException {
                                HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
                                HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();

                                for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
                                    spanDecorator.onError(httpRequest,
                                            httpResponse,
                                            event.getThrowable(),
                                            span);
                                }
                            }

                            @Override
                            public void onStartAsync(AsyncEvent event) throws IOException {
                            }
                        });
            } else {

                span.finish();
            }
        }
    }
}

通过上述代码,可以发现有一个满脸长着我是扩展点的东西,就是ServletFilterSpanDecorator,其主要负责在过滤器链执行之前,之后以及发生异常时对Span进行装饰,怎么理解这里的装饰呢,其实就是往Span添加一些东西或者修改一些东西,举个例,在过滤器链执行前,往SpanTags中添加本次请求的URI,然后在过滤器链执行后,往SpanTags中添加本次请求的响应码,等等这些需求,都可以在ServletFilterSpanDecorator中完成,这其实就赋予了我们对Span极高的可操作性。

装饰器除了在TracingFilter中有被使用,同样也在Opentracing提供的TracingRestTemplateInterceptor中被使用,其intercept() 方法如下所示。

@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body,
                                    ClientHttpRequestExecution execution) throws IOException {
    ClientHttpResponse httpResponse;

    Span span = tracer.buildSpan(httpRequest.getMethod().toString())
            .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
            .start();
    tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS,
            new HttpHeadersCarrier(httpRequest.getHeaders()));

    for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
        try {
            spanDecorator.onRequest(httpRequest, span);
        } catch (RuntimeException exDecorator) {
            log.error("Exception during decorating span", exDecorator);
        }
    }

    try (Scope scope = tracer.activateSpan(span)) {
        httpResponse = execution.execute(httpRequest, body);
        for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
            try {
                spanDecorator.onResponse(httpRequest, httpResponse, span);
            } catch (RuntimeException exDecorator) {
                log.error("Exception during decorating span", exDecorator);
            }
        }
    } catch (Exception ex) {
        for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
            try {
                spanDecorator.onError(httpRequest, ex, span);
            } catch (RuntimeException exDecorator) {
                log.error("Exception during decorating span", exDecorator);
            }
        }
        throw ex;
    } finally {
        span.finish();
    }

    return httpResponse;
}

上述实现中,在请求前,请求后以及报错时使用了RestTemplateSpanDecorator来装饰Span,所以RestTemplateSpanDecorator也是一个重要的扩展点,具体如何使用,在后续的文章中会逐步进行演示。

2. Injector和Extractor

InjectorExtractor分别用来处理SpanContext的注入和提取操作,以Jaeger为例,在创建JaegerTracer时,可以按照键值对的方式,向JaegerTracer注册InjectorExtractor,就像下面这样。

@Configuration
public class TracerConfig {

    @Autowired
    private SpanReporter spanReporter;

    @Autowired
    private Sampler sampler;

    @Bean
    public Tracer tracer(MyHttpHeadersInjector myHttpHeadersInjector,
                         MyHttpHeadersExtractor myHttpHeadersExtractor) {
        return new JaegerTracer.Builder(TRACER_SERVICE_NAME)
                .withTraceId128Bit()
                .withSampler(sampler)
                .withReporter(spanReporter)
                .registerInjector(Format.Builtin.HTTP_HEADERS, myHttpHeadersInjector)
                .registerExtractor(Format.Builtin.HTTP_HEADERS, myHttpHeadersExtractor)
                .build();
    }

}

键是Format,可以自己定义,也可以使用Opentracing为我们定义好的,例如Format.Builtin#HTTP_HEADERS,值就是InjectorExtractor的实现类,那么我们就可以自己提供InjectorExtractor的实现类来扩展SpanContext的注入和提取操作。

3. ScopeManager

大多数情况下,Opentracing提供的ThreadLocalScopeManager能满足我们的使用需求,但如果是异步链路追踪的场景,ThreadLocal就无法满足使用需求,此时需要使用InheritableThreadLocal,我们就可以基于InheritableThreadLocal来提供一个ScopeManager接口的实现类,并在创建Tracer时指定我们要使用的ScopeManager。还是以Jaeger为例,在创建JaegerTracer时,可以指定要使用的ScopeManager,如下所示。

@Configuration
public class TracerConfig {

    @Autowired
    private SpanReporter spanReporter;

    @Autowired
    private Sampler sampler;

    @Bean
    public Tracer tracer(ScopeManager scopeManager) {
        return new JaegerTracer.Builder(TRACER_SERVICE_NAME)
                .withTraceId128Bit()
                .withSampler(sampler)
                .withReporter(spanReporter)
                .withScopeManager(scopeManager)
                .build();
    }

}

具体如何使用,以及如何基于InheritableThreadLocal来实现适用于异步链路追踪的ScopeManager,这里就不再深入,这一块儿将在后续的文章中进行分析和演示。

4. 其它扩展点

扩展点还有很多,限于本文篇幅,这里就不再一一介绍,后面在实现分布式链路工具包的时候,用到了,自然会进行说明。

总结

一不小心,又扯了这么多,本文其实重点就是聚焦于Opentracing中的若干重要概念,这里用于一张图进行总结吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值