1. 程式人生 > >spring-cloud-sleuth+zipkin原始碼探究

spring-cloud-sleuth+zipkin原始碼探究

1.1. 前言

  粗略看了下spring cloud sleuth core原始碼,發現內容真的有點多,它支援了很多型別的鏈路追蹤,我就找其中一個比較有代表性的深入剖析下原始碼結構和內容

1.2. spring-cloud-sleuth-core原始碼解析

1.2.1. 結構

  1. 可以看到原始碼中支援的追蹤型別有很多,支援async,hystrix,websocket,rxjava,Spring mvc,servlet,spring restTemplate,feign,zuul等等,這裡我著重探討spring web mvc的鏈路追蹤
  2. 開啟web包,找到TraceWebAutoConfiguration,這裡配置了主要的初始化類

1.2.2. 過濾器註冊

  1. 當啟動初始化程式時,跟蹤程式碼如下
    @Bean
    public FilterRegistrationBean traceWebFilter(TraceFilter traceFilter) {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
                traceFilter);
        filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE,
                REQUEST);
        filterRegistrationBean.setOrder(TraceFilter.ORDER);
        return filterRegistrationBean;
    }

    @Bean
    @ConditionalOnMissingBean
    public TraceFilter traceFilter(BeanFactory beanFactory,
            SkipPatternProvider skipPatternProvider) {
        return new TraceFilter(beanFactory, skipPatternProvider.skipPattern());
    }
  1. 初始化traceFilter,進行過濾器註冊

1.2.3. 攔截器註冊

  1. 然後看TraceWebMvcConfigurer類,它會進行攔截器的註冊
@Configuration
class TraceWebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Autowired BeanFactory beanFactory;

    @Bean
    public TraceHandlerInterceptor traceHandlerInterceptor(BeanFactory beanFactory) {
        return new TraceHandlerInterceptor(beanFactory);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.beanFactory.getBean(TraceHandlerInterceptor.class));
    }
}
  1. TraceHandlerInterceptor類中,preHandle,afterCompletion方法可以看出,這是對請求進行攔截進行span的包裝
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        String spanName = spanName(handler);
        boolean continueSpan = getRootSpanFromAttribute(request) != null;
        Span span = continueSpan ? getRootSpanFromAttribute(request) : getTracer().createSpan(spanName);
        if (log.isDebugEnabled()) {
            log.debug("Handling span " + span);
        }
        addClassMethodTag(handler, span);
        addClassNameTag(handler, span);
        setSpanInAttribute(request, span);
        if (!continueSpan) {
            setNewSpanCreatedAttribute(request, span);
        }
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
        if (isErrorControllerRelated(request)) {
            if (log.isDebugEnabled()) {
                log.debug("Skipping closing of a span for error controller processing");
            }
            return;
        }
        Span span = getRootSpanFromAttribute(request);
        if (ex != null) {
            getErrorParser().parseErrorTags(span, ex);
        }
        if (getNewSpanFromAttribute(request) != null) {
            if (log.isDebugEnabled()) {
                log.debug("Closing span " + span);
            }
            Span newSpan = getNewSpanFromAttribute(request);
            getTracer().continueSpan(newSpan);
            getTracer().close(newSpan);
            clearNewSpanCreatedAttribute(request);
        }
    }

1.2.4. zipkin端點提交

  1. 這裡首先會初始化HttpZipkinSpanReporter類,,用來進行span端點提交,然後初始化ZipkinSpanListenerspan的監聽器,用來監聽並呼叫端點提交,以上配置再下圖位置

1.2.5. 呼叫http介面時,進入過濾器

  1. 首先進入TraceFilter中的過濾方法doFilter,這裡會做span的建立
private Span createSpan(HttpServletRequest request,
            boolean skip, Span spanFromRequest, String name) {
        if (spanFromRequest != null) {
            if (log.isDebugEnabled()) {
                log.debug("Span has already been created - continuing with the previous one");
            }
            return spanFromRequest;
        }
        //加入呼叫鏈路ZipkinHttpSpanExtractor,此鏈路在TraceHttpAutoConfiguration中配置例項化,呼叫鏈還沒有時,返回為空,作為頭節點
        Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));
        if (parent != null) {
            if (log.isDebugEnabled()) {
                log.debug("Found a parent span " + parent + " in the request");
            }
            addRequestTagsForParentSpan(request, parent);
            spanFromRequest = parent;
            tracer().continueSpan(spanFromRequest);
            if (parent.isRemote()) {
                parent.logEvent(Span.SERVER_RECV);
            }
            request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
            if (log.isDebugEnabled()) {
                log.debug("Parent span is " + parent + "");
            }
        } else {
            if (skip) {
                spanFromRequest = tracer().createSpan(name, NeverSampler.INSTANCE);
            }
            else {
                String header = request.getHeader(Span.SPAN_FLAGS);
                if (Span.SPAN_SAMPLED.equals(header)) {
                    spanFromRequest = tracer().createSpan(name, new AlwaysSampler());
                } else {
                //建立span節點
                    spanFromRequest = tracer().createSpan(name);
                }
            }
            spanFromRequest.logEvent(Span.SERVER_RECV);
            request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
            if (log.isDebugEnabled()) {
                log.debug("No parent span present - creating a new span");
            }
        }
        return spanFromRequest;
    }

1.2.6. 進入攔截器

  1. preHandle方法中,對span進行包裝,然後把span放入請求頭header中
  2. 最後再DefaultTracer中進行span的關閉和spanReporter