From 0cd1441f9b62d7b2aa3251dd951e289192c14885 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 22 Feb 2016 18:29:52 +0100 Subject: [PATCH] Refactored the async tracing --- ...eAsyncClientHttpRequestFactoryWrapper.java | 74 +++++++++++++++++-- .../TraceWebAsyncClientAutoConfiguration.java | 54 ++++++++++++++ .../TraceWebClientAutoConfiguration.java | 29 -------- .../main/resources/META-INF/spring.factories | 1 + 4 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java index 8177f7128..c00cdd8b3 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java @@ -20,28 +20,78 @@ import java.io.IOException; import java.net.URI; import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.http.HttpMethod; import org.springframework.http.client.AsyncClientHttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** - * Wrapper that adds trace related headers to the created AsyncClientHttpRequest - * - * @see org.springframework.web.client.RestTemplate - * @see SpanAccessor + * Wrapper that adds trace related headers to the created {@link AsyncClientHttpRequest} + * and to the {@link ClientHttpRequest} * * @author Marcin Grzejszczak * @author Spencer Gibb */ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttpRequestInterceptor - implements AsyncClientHttpRequestFactory { + implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory { + private final Tracer tracer; private final AsyncClientHttpRequestFactory delegate; + private final ClientHttpRequestFactory syncDelegate; - public TraceAsyncClientHttpRequestFactoryWrapper(SpanAccessor accessor, + /** + * According to the javadocs all Spring {@link AsyncClientHttpRequestFactory} implement + * the {@link ClientHttpRequestFactory} interface. + * + * In case that it's not true we're setting the {@link SimpleClientHttpRequestFactory} + * as a default for sync request processing. + * + * @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory) + */ + public TraceAsyncClientHttpRequestFactoryWrapper(SpanAccessor accessor, Tracer tracer, AsyncClientHttpRequestFactory delegate) { super(accessor); + this.tracer = tracer; this.delegate = delegate; + this.syncDelegate = delegate instanceof ClientHttpRequestFactory ? + (ClientHttpRequestFactory) delegate : defaultClientHttpRequestFactory(); + } + + /** + * Default implementation that creates a {@link SimpleClientHttpRequestFactory} that + * has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor} + */ + public TraceAsyncClientHttpRequestFactoryWrapper(SpanAccessor accessor, Tracer tracer) { + super(accessor); + this.tracer = tracer; + SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory(); + this.delegate = simpleClientHttpRequestFactory; + this.syncDelegate = simpleClientHttpRequestFactory; + } + + public TraceAsyncClientHttpRequestFactoryWrapper(SpanAccessor accessor, Tracer tracer, + AsyncClientHttpRequestFactory delegate, ClientHttpRequestFactory syncDelegate) { + super(accessor); + this.tracer = tracer; + this.delegate = delegate; + this.syncDelegate = syncDelegate; + } + + private SimpleClientHttpRequestFactory defaultClientHttpRequestFactory() { + SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory(); + simpleClientHttpRequestFactory.setTaskExecutor(asyncListenableTaskExecutor(this.tracer)); + return simpleClientHttpRequestFactory; + } + + private AsyncListenableTaskExecutor asyncListenableTaskExecutor(Tracer tracer) { + ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.initialize(); + return new TraceAsyncListenableTaskExecutor(threadPoolTaskScheduler, tracer); } @Override @@ -55,4 +105,16 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp publishStartEvent(request); return request; } + + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) + throws IOException { + ClientHttpRequest request = this.syncDelegate.createRequest(uri, httpMethod); + if (!isTracing()) { + doNotSampleThisSpan(request); + return request; + } + publishStartEvent(request); + return request; + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java new file mode 100644 index 000000000..7c883afc6 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.web.client; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.AsyncClientHttpRequestFactory; +import org.springframework.web.client.AsyncRestTemplate; + +/** + * @author Marcin Grzejszczak + */ +@Configuration +@ConditionalOnProperty(value = "spring.sleuth.async.client.enabled", matchIfMissing = true) +@ConditionalOnClass(AsyncRestTemplate.class) +@ConditionalOnBean(SpanAccessor.class) +@AutoConfigureAfter(TraceAutoConfiguration.class) +public class TraceWebAsyncClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(SpanAccessor spanAccessor, Tracer tracer) { + return new TraceAsyncClientHttpRequestFactoryWrapper(spanAccessor, tracer); + } + + @Bean + @ConditionalOnMissingBean + public AsyncRestTemplate asyncRestTemplate(AsyncClientHttpRequestFactory asyncClientHttpRequestFactory) { + return new AsyncRestTemplate(asyncClientHttpRequestFactory); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index 2754312e5..d1a231204 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -29,16 +29,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.sleuth.SpanAccessor; -import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.AsyncListenableTaskExecutor; -import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.web.client.AsyncRestTemplate; import org.springframework.web.client.RestTemplate; /** @@ -63,29 +57,6 @@ public class TraceWebClientAutoConfiguration { return new RestTemplate(); } - - @Bean - @ConditionalOnMissingBean - public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(Tracer tracer) { - SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory(); - simpleClientHttpRequestFactory.setTaskExecutor(asyncListenableTaskExecutor(tracer)); - return simpleClientHttpRequestFactory; - } - - private AsyncListenableTaskExecutor asyncListenableTaskExecutor(Tracer tracer) { - ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); - threadPoolTaskScheduler.initialize(); - return new TraceAsyncListenableTaskExecutor(threadPoolTaskScheduler, tracer); - } - - @Bean - @ConditionalOnMissingBean - public AsyncRestTemplate asyncRestTemplate(SpanAccessor spanAccessor, - AsyncClientHttpRequestFactory asyncClientHttpRequestFactory, RestTemplate restTemplate) { - return new AsyncRestTemplate(new TraceAsyncClientHttpRequestFactoryWrapper( - spanAccessor, asyncClientHttpRequestFactory), restTemplate); - } - @Configuration protected static class TraceInterceptorConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories index 068cff0ff..8292270a7 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories @@ -11,6 +11,7 @@ org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfigurati org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ 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.TraceFeignClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration