opentracing-java-spring-jaeger源码分析(SpringCloud Jaeger实现)

Jaeger logo

背景:公司内部的RPC框架,需要接入Jaeger, 只能自己实现了下OpenTracing; 分析、借鉴了下 opentracing-contrib/java-spring-jaeger,记录一下。

其实 java-spring-jaeger web 仓库/依赖,其实只是一个SpringBoot Starter,使其自动注入了依赖,并且注册了拦截器, 拦截器原理主要是依赖 opentracing-contrib/java-spring-web

下面我们分析下其源码;

一、主要依赖代码

git地址: https://github.com/opentracing-contrib/java-spring-jaeger

java 依赖:

<dependency>
  <groupId>io.opentracing.contrib</groupId>
  <artifactId>opentracing-spring-jaeger-web-starter</artifactId>
</dependency>

引用关系: opentracing-spring-web-starter -> opentracing-spring-web

starter 描述了注册加载Bean逻辑, spring-web 为真正的spring web 拦截逻辑 ;

二、 是如何实现的?

2.1 实现逻辑

  1. 服务端: ServerTracingAutoConfiguration -> tracingFilter(), tracingHandlerInterceptor() -> TracingFilter.class, TracingHandlerInterceptor.class

  2. 客户端

    • TracingRestTemplateInterceptor.class: restTemplate 拦截器

    • TracingAsyncRestTemplateInterceptor.class: AsyncRestTemplate拦截器

    • TracingWebClientBeanPostProcessor.class : webClient 拦截器

    • TracingWebFilter.class: Tracing {@link WebFilter} for Spring WebFlux.

2.2 客户端/服务端实现代码

下面已 TracingRestTemplateInterceptor 为例,列出来给大家看下拦截器的主要逻辑;

主要模块设计请见下图, 图片来自网络,链接:https://www.jianshu.com/p/82cd923191fb

OpenTracing Java 主要实现模块

2.1 客户端标准实现

Span span = tracer.buildSpan("...").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
   span.setTag("...", "...");
	//... 业务调用逻辑
} catch (Exception e) {
   span.log(...);
} finally {
    // Optionally finish the Span if the operation it represents
    //is logically completed at this point.
    span.finish();
}

2.2 服务端标准实现

/**
* If request is traced then do not start new span.
*/
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 all exceptions (e.g. RuntimeException, ServletException...)
    } catch (Throwable ex) {
        for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
            spanDecorator.onError(httpRequest, httpResponse, ex, span);
        }
        throw ex;
    } finally {
        if (httpRequest.isAsyncStarted()) {
            // what if async is already finished? This would not be called
            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 {
            // If not async, then need to explicitly finish the span associated with the scope.
            // This is necessary, as we don't know whether this request is being handled
            // asynchronously until after the scope has already been started.
            span.finish();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值