Premature async rest template (#479)

without this change the async rest template provides wrong value of the span duration
with this change the span is closed via a callback

fixes #475
This commit is contained in:
Marcin Grzejszczak
2016-12-16 13:59:03 +01:00
parent 73fb8a8c4c
commit 964afe10f7
2 changed files with 60 additions and 23 deletions

View File

@@ -20,11 +20,13 @@ import java.net.URI;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.util.ExceptionUtils;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.AsyncClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.client.AsyncRequestCallback;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.ResponseExtractor;
@@ -75,27 +77,62 @@ public class TraceAsyncRestTemplate extends AsyncRestTemplate {
protected <T> ListenableFuture<T> doExecute(URI url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor)
throws RestClientException {
try {
return super.doExecute(url, method, requestCallback, responseExtractor);
} finally {
ListenableFuture<T> future = super.doExecute(url, method, requestCallback, responseExtractor);
Span span = this.tracer.getCurrentSpan();
future.addCallback(new TraceListenableFutureCallback<>(this.tracer, span));
// potential race can happen here
if (span != null && span.equals(this.tracer.getCurrentSpan())) {
this.tracer.detach(span);
}
return future;
}
private static class TraceListenableFutureCallback<T> implements ListenableFutureCallback<T> {
private final Tracer tracer;
private final Span parent;
private TraceListenableFutureCallback(Tracer tracer, Span parent) {
this.tracer = tracer;
this.parent = parent;
}
@Override
public void onFailure(Throwable ex) {
continueSpan();
this.tracer.addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(ex));
finish();
}
}
private void finish() {
if (!isTracing()) {
return;
@Override
public void onSuccess(T result) {
continueSpan();
finish();
}
private void continueSpan() {
this.tracer.continueSpan(this.parent);
}
private void finish() {
if (!isTracing()) {
return;
}
currentSpan().logEvent(Span.CLIENT_RECV);
this.tracer.close(currentSpan());
}
private Span currentSpan() {
return this.tracer.getCurrentSpan();
}
private boolean isTracing() {
return this.tracer.isTracing();
}
currentSpan().logEvent(Span.CLIENT_RECV);
this.tracer.close(this.currentSpan());
}
private Span currentSpan() {
return this.tracer.getCurrentSpan();
}
private boolean isTracing() {
return this.tracer.isTracing();
}
}

View File

@@ -49,11 +49,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DirtiesContext
public class RestTemplateTraceAspectIntegrationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private AspectTestingController controller;
@Autowired WebApplicationContext context;
@Autowired AspectTestingController controller;
@Autowired Tracer tracer;
private MockMvc mockMvc;
@@ -61,12 +59,14 @@ public class RestTemplateTraceAspectIntegrationTests {
public void init() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
this.controller.reset();
TestSpanContextHolder.removeCurrentSpan();
ExceptionUtils.setFail(true);
}
@Before
@After
public void cleanup() {
TestSpanContextHolder.removeCurrentSpan();
public void verify() {
then(this.tracer.getCurrentSpan()).isNull();
then(ExceptionUtils.getLastException()).isNull();
}
@Test