spring-cloud-sleuth+zipkin源碼探究


1. 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的提交

參考:https://blog.csdn.net/zhllansezhilian/article/details/83001870


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM