基於spring cloud 1.2.1版本java
spring cloud的基礎是spring boot,而spring boot能夠零配置初始化spring。當咱們引入spring-cloud-starter-sleuth依賴的時候,會附帶spring-cloud-sleuth-core依賴。spring boot掃描spring-cloud-sleuth-core依賴下的spring.factories文件,以下:web
# Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration,\ org.springframework.cloud.sleuth.metric.TraceMetricsAutoConfiguration,\ org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncCustomAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\ org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration # Environment Post Processor org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor
這些是自動裝配bean的javaConfig,下面首先分析TraceAutoConfiguration。spring
@Configuration @ConditionalOnProperty(value="spring.sleuth.enabled", matchIfMissing=true) @EnableConfigurationProperties({TraceKeys.class, SleuthProperties.class}) public class TraceAutoConfiguration { @Autowired SleuthProperties properties; @Bean @ConditionalOnMissingBean public Random randomForSpanIds() { return new Random(); } @Bean @ConditionalOnMissingBean public Sampler defaultTraceSampler() { return NeverSampler.INSTANCE; } @Bean @ConditionalOnMissingBean(Tracer.class) public DefaultTracer sleuthTracer(Sampler sampler, Random random, SpanNamer spanNamer, SpanLogger spanLogger, SpanReporter spanReporter, TraceKeys traceKeys) { return new DefaultTracer(sampler, random, spanNamer, spanLogger, spanReporter, this.properties.isTraceId128(), traceKeys); } @Bean @ConditionalOnMissingBean public SpanNamer spanNamer() { return new DefaultSpanNamer(); } @Bean @ConditionalOnMissingBean public SpanReporter defaultSpanReporter() { return new NoOpSpanReporter(); } @Bean @ConditionalOnMissingBean public SpanAdjuster defaultSpanAdjuster() { return new NoOpSpanAdjuster(); } }
@Configuration與@Bean結合起來使用能夠以javaConfig方式生成bean,@Bean annotation下面的方法會生成一個bean,方法名爲bean的name,而入參則是建立該bean依賴的的ref bean。
@ConditionalOnProperty(value="spring.sleuth.enabled", matchIfMissing=true)註解的意思是該類生成bean須要的條件,這裏條件是當spring.sleuth.enabled爲true時(沒有設置默認爲true即條件符合,也就是顯式設置爲false纔算不符合條件),纔會觸發生成bean。spring.sleuth.enabled的值在SleuthProperties中設置的,它會讀取配置文件中以spring.sleuth爲前綴的信息並設置到field中,好比enabled等。apache
@ConfigurationProperties("spring.sleuth") public class SleuthProperties { private boolean enabled = true; /** When true, generate 128-bit trace IDs instead of 64-bit ones. */ private boolean traceId128 = false; public boolean isEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isTraceId128() { return this.traceId128; } public void setTraceId128(boolean traceId128) { this.traceId128 = traceId128; } }
@EnableConfigurationProperties({TraceKeys.class, SleuthProperties.class})註解則是支持將@ConfigurationProperties註解的bean註冊到本類當中,這裏是將TraceKey、SleuthProperties對應的bean註冊到本類當中。websocket
下面分析每一個@Bean annotaded method,首先解釋下@ConditionalOnMissingBean的含義。它的意思很明顯,就是當不存在該類型的bean時纔會觸發。若是在其餘地方定義了此類型的bean,那就不會執行該方法產生bean了。mvc
sleuth的基礎config分析完後,下面分析instrumnet的配置,即如何在應用中織入trace埋點的配置。app
正經常使用到的trace span有三種類型:dom
一、2中的span屬於remote span,而3中的span屬於local span。異步
好的,下面分析web的基礎配置,也就是涉及到跨節點的trace配置。socket
@Configuration @ConditionalOnBean(Tracer.class) @AutoConfigureAfter(TraceAutoConfiguration.class) @EnableConfigurationProperties({ TraceKeys.class, SleuthWebProperties.class }) public class TraceHttpAutoConfiguration { @Bean @ConditionalOnMissingBean public HttpTraceKeysInjector httpTraceKeysInjector(Tracer tracer, TraceKeys traceKeys) { return new HttpTraceKeysInjector(tracer, traceKeys); } @Bean @ConditionalOnMissingBean public HttpSpanExtractor httpSpanExtractor(SleuthWebProperties sleuthWebProperties) { return new ZipkinHttpSpanExtractor(Pattern.compile(sleuthWebProperties.getSkipPattern())); } @Bean @ConditionalOnMissingBean public HttpSpanInjector httpSpanInjector() { return new ZipkinHttpSpanInjector(); } }
@ConditionalOnBean(Tracer.class)意思是當存在Trace bean的時候纔會觸發。
@AutoConfigureAfter(TraceAutoConfiguration.class)意思是在TraceAutoConfiguration處理完後再觸發。
@EnableConfigurationProperties({ TraceKeys.class, SleuthWebProperties.class })意思是將TraceKey、SleuthWebProperties對應的config注入到該類中,以便後期直接使用這些java config對象。
前面分析的TraceHttpAutoConfiguration生成的bean只是一些核心基礎的bean,但沒有具體應用到web服務當中。而此類會進行相關配置。
@Configuration @ConditionalOnProperty(value = "spring.sleuth.web.enabled", matchIfMissing = true) @ConditionalOnWebApplication @ConditionalOnBean(Tracer.class) @AutoConfigureAfter(TraceHttpAutoConfiguration.class) public class TraceWebAutoConfiguration { /** * Nested config that configures Web MVC if it's present (without adding a runtime * dependency to it) */ @Configuration @ConditionalOnClass(WebMvcConfigurerAdapter.class) @Import(TraceWebMvcConfigurer.class) protected static class TraceWebMvcAutoConfiguration { } @Bean public TraceWebAspect traceWebAspect(Tracer tracer, TraceKeys traceKeys, SpanNamer spanNamer) { return new TraceWebAspect(tracer, spanNamer, traceKeys); } @Bean @ConditionalOnClass(name = "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping") public TraceSpringDataBeanPostProcessor traceSpringDataBeanPostProcessor( BeanFactory beanFactory) { return new TraceSpringDataBeanPostProcessor(beanFactory); } @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 public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys, SkipPatternProvider skipPatternProvider, SpanReporter spanReporter, HttpSpanExtractor spanExtractor, HttpTraceKeysInjector httpTraceKeysInjector) { return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(), spanReporter, spanExtractor, httpTraceKeysInjector); } @Configuration @ConditionalOnClass(ManagementServerProperties.class) @ConditionalOnMissingBean(SkipPatternProvider.class) @EnableConfigurationProperties(SleuthWebProperties.class) protected static class SkipPatternProviderConfig { @Bean @ConditionalOnBean(ManagementServerProperties.class) public SkipPatternProvider skipPatternForManagementServerProperties( final ManagementServerProperties managementServerProperties, final SleuthWebProperties sleuthWebProperties) { return new SkipPatternProvider() { @Override public Pattern skipPattern() { return getPatternForManagementServerProperties( managementServerProperties, sleuthWebProperties); } }; } /** * Sets or appends {@link ManagementServerProperties#getContextPath()} to the skip * pattern. If neither is available then sets the default one */ static Pattern getPatternForManagementServerProperties( ManagementServerProperties managementServerProperties, SleuthWebProperties sleuthWebProperties) { String skipPattern = sleuthWebProperties.getSkipPattern(); if (StringUtils.hasText(skipPattern) && StringUtils.hasText(managementServerProperties.getContextPath())) { return Pattern.compile(skipPattern + "|" + managementServerProperties.getContextPath() + ".*"); } else if (StringUtils.hasText(managementServerProperties.getContextPath())) { return Pattern .compile(managementServerProperties.getContextPath() + ".*"); } return defaultSkipPattern(skipPattern); } @Bean @ConditionalOnMissingBean(ManagementServerProperties.class) public SkipPatternProvider defaultSkipPatternBeanIfManagementServerPropsArePresent(SleuthWebProperties sleuthWebProperties) { return defaultSkipPatternProvider(sleuthWebProperties.getSkipPattern()); } } @Bean @ConditionalOnMissingClass("org.springframework.boot.actuate.autoconfigure.ManagementServerProperties") @ConditionalOnMissingBean(SkipPatternProvider.class) public SkipPatternProvider defaultSkipPatternBean(SleuthWebProperties sleuthWebProperties) { return defaultSkipPatternProvider(sleuthWebProperties.getSkipPattern()); } private static SkipPatternProvider defaultSkipPatternProvider( final String skipPattern) { return new SkipPatternProvider() { @Override public Pattern skipPattern() { return defaultSkipPattern(skipPattern); } }; } private static Pattern defaultSkipPattern(String skipPattern) { return StringUtils.hasText(skipPattern) ? Pattern.compile(skipPattern) : Pattern.compile(SleuthWebProperties.DEFAULT_SKIP_PATTERN); } interface SkipPatternProvider { Pattern skipPattern(); } }
TraceWebAutoConfiguration執行的條件有這些:
內部靜態類TraceWebMvcAutoConfiguration是爲了import TraceWebMvcConfigurer的配置,它的條件是存在WebMvcConfigurerAdapter類,此類是spring mvc包中提供給用戶自定義攔截器等功能的抽象類。
下面咱們看下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)); } }
TraceWebMvcConfigurer定義了一個TraceHandlerInterceptor攔截器,而後重寫addInterceptors方法,將攔截器設置到了spring mvc生命週期中。後期會對TraceHandlerInterceptor攔截器進行分析。
接下來分析TraceWebAutoConfiguration生成的每一個bean。
@Aspect public class TraceWebAspect { private static final Log log = org.apache.commons.logging.LogFactory .getLog(TraceWebAspect.class); private final Tracer tracer; private final SpanNamer spanNamer; private final TraceKeys traceKeys; public TraceWebAspect(Tracer tracer, SpanNamer spanNamer, TraceKeys traceKeys) { this.tracer = tracer; this.spanNamer = spanNamer; this.traceKeys = traceKeys; } @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") private void anyRestControllerAnnotated() { }// NOSONAR @Pointcut("@within(org.springframework.stereotype.Controller)") private void anyControllerAnnotated() { } // NOSONAR @Pointcut("execution(public java.util.concurrent.Callable *(..))") private void anyPublicMethodReturningCallable() { } // NOSONAR @Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningCallable()") private void anyControllerOrRestControllerWithPublicAsyncMethod() { } // NOSONAR @Pointcut("execution(public org.springframework.web.context.request.async.WebAsyncTask *(..))") private void anyPublicMethodReturningWebAsyncTask() { } // NOSONAR @Pointcut("execution(public * org.springframework.web.servlet.HandlerExceptionResolver.resolveException(..)) && args(request, response, handler, ex)") private void anyHandlerExceptionResolver(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { } // NOSONAR @Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningWebAsyncTask()") private void anyControllerOrRestControllerWithPublicWebAsyncTaskMethod() { } // NOSONAR @Around("anyControllerOrRestControllerWithPublicAsyncMethod()") @SuppressWarnings("unchecked") public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { Callable<Object> callable = (Callable<Object>) pjp.proceed(); if (this.tracer.isTracing()) { if (log.isDebugEnabled()) { log.debug("Wrapping callable with span [" + this.tracer.getCurrentSpan() + "]"); } return new SpanContinuingTraceCallable<>(this.tracer, this.traceKeys, this.spanNamer, callable); } else { return callable; } } @Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()") public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { final WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) pjp.proceed(); if (this.tracer.isTracing()) { try { if (log.isDebugEnabled()) { log.debug("Wrapping callable with span [" + this.tracer.getCurrentSpan() + "]"); } Field callableField = WebAsyncTask.class.getDeclaredField("callable"); callableField.setAccessible(true); callableField.set(webAsyncTask, new SpanContinuingTraceCallable<>(this.tracer, this.traceKeys, this.spanNamer, webAsyncTask.getCallable())); } catch (NoSuchFieldException ex) { log.warn("Cannot wrap webAsyncTask's callable with TraceCallable", ex); } } return webAsyncTask; } @Around("anyHandlerExceptionResolver(request, response, handler, ex)") public Object markRequestForSpanClosing(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Throwable { Span currentSpan = this.tracer.getCurrentSpan(); try { if (!currentSpan.tags().containsKey(Span.SPAN_ERROR_TAG_NAME)) { this.tracer.addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(ex)); } return pjp.proceed(); } finally { if (log.isDebugEnabled()) { log.debug("Marking span " + currentSpan + " for closure by Trace Filter"); } request.setAttribute(TraceFilter.TRACE_CLOSE_SPAN_REQUEST_ATTR, true); } }
此bean對兩種狀況進行了aop攔截:
首先判斷是否存在org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping,便是否引入了spring data rest相關依賴包。沒有的話則不會生成該bean。
該bean的主要做用是對Spring Data REST Controllers進行了包裝,生成TraceDelegatingHandlerMapping代理類,該類對被代理類進行了攔截器設置,攔截器爲前部分講到的TraceHandlerInterceptor。
trace攔截器,對每一個請求進行攔截,在spring mvc的攔截器以前觸發。他的主要做用是從request head中獲取trace信息,生成span,沒有trace信息則生成一個新的span。
將traceFilter註冊到servlet容器中。也就是用java config形式代替了在web.xml中配置filter。
spring sleuth的核心配置分析完後,下面分析本章最後的部分-ZipkinAutoConfiguration。
@Configuration @EnableConfigurationProperties({ZipkinProperties.class, SamplerProperties.class}) @ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true) @AutoConfigureBefore(TraceAutoConfiguration.class) public class ZipkinAutoConfiguration { @Bean @ConditionalOnMissingBean public ZipkinSpanReporter reporter(SpanMetricReporter spanMetricReporter, ZipkinProperties zipkin, ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) { RestTemplate restTemplate = new RestTemplate(); zipkinRestTemplateCustomizer.customize(restTemplate); return new HttpZipkinSpanReporter(restTemplate, zipkin.getBaseUrl(), zipkin.getFlushInterval(), spanMetricReporter); } @Bean @ConditionalOnMissingBean public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) { return new DefaultZipkinRestTemplateCustomizer(zipkinProperties); } @Bean @ConditionalOnMissingBean public Sampler defaultTraceSampler(SamplerProperties config) { return new PercentageBasedSampler(config); } @Bean public SpanReporter zipkinSpanListener(ZipkinSpanReporter reporter, EndpointLocator endpointLocator, Environment environment, List<SpanAdjuster> spanAdjusters) { return new ZipkinSpanListener(reporter, endpointLocator, environment, spanAdjusters); } @Configuration @ConditionalOnMissingBean(EndpointLocator.class) @ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "false", matchIfMissing = true) protected static class DefaultEndpointLocatorConfiguration { @Autowired(required=false) private ServerProperties serverProperties; @Autowired private ZipkinProperties zipkinProperties; @Autowired(required=false) private InetUtils inetUtils; @Value("${spring.application.name:unknown}") private String appName; @Bean public EndpointLocator zipkinEndpointLocator() { return new ServerPropertiesEndpointLocator(this.serverProperties, this.appName, this.zipkinProperties, this.inetUtils); } } @Configuration @ConditionalOnClass(DiscoveryClient.class) @ConditionalOnMissingBean(EndpointLocator.class) @ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "true") protected static class DiscoveryClientEndpointLocatorConfiguration { @Autowired(required=false) private ServerProperties serverProperties; @Autowired private ZipkinProperties zipkinProperties; @Autowired(required=false) private InetUtils inetUtils; @Value("${spring.application.name:unknown}") private String appName; @Autowired(required=false) private DiscoveryClient client; @Bean public EndpointLocator zipkinEndpointLocator() { return new FallbackHavingEndpointLocator(discoveryClientEndpointLocator(), new ServerPropertiesEndpointLocator(this.serverProperties, this.appName, this.zipkinProperties, this.inetUtils)); } private DiscoveryClientEndpointLocator discoveryClientEndpointLocator() { if (this.client!=null) { return new DiscoveryClientEndpointLocator(this.client, this.zipkinProperties); } return null; } } }
ZipkinAutoConfiguration上的annotation就不一一說明,前部分已經涉及到了。下面直接分析生成的bean。
返回HttpZipkinSpanReporter類型bean,內部會初始化sender以及delegate。sender是以何種方式將zipkin span發送到zipkin server。delegate是一個委託類,內部建立一個有界隊列,異步將zipkin span發送到zipkin server。
建立reporter bean時須要依賴的sender。這裏zipkin使用restTemplate做爲sender提交span。
默認採樣器,以前在TraceAutoConfiguration中也聲明建立defaultTraceSampler,但因爲ZipkinAutoConfiguration上有這樣的註解:@AutoConfigureBefore(TraceAutoConfiguration.class),意思是在TraceAutoConfiguration以前執行,因此默認採樣器使用的是ZipkinAutoConfiguration中生成的採樣器即PercentageBasedSampler,它能夠設置百分比來進行採樣。
sleuth event監聽器,上報sleuth span,而後轉換成zipkin span,再經過zipkin reporter上報到zipkin server。
本章就到此結束了,其實還有hystrix、async相關模塊的配置沒講,本章只講了web相關的,後期有機會再講。