From 6ecfbca6ed6bcd38e715f03ca5d5a513c9593346 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 23 Dec 2015 18:08:09 +0100 Subject: [PATCH] [#39] Initial approach to Hystrix concurrency strategy --- .../SleuthHystrixConcurrencyStrategy.java | 24 ++++ .../hystrix/SleuthHystrixConfiguration.java | 19 +++ .../main/resources/META-INF/spring.factories | 1 + .../instrument/hystrix/JavanicaITest.java | 125 ++++++++++++++++++ .../instrument/web/TraceAsyncITest.java | 13 +- 5 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConfiguration.java create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/JavanicaITest.java diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java new file mode 100644 index 000000000..89f555a51 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java @@ -0,0 +1,24 @@ +package org.springframework.cloud.sleuth.instrument.hystrix; + +import java.util.concurrent.Callable; + +import org.springframework.cloud.sleuth.TraceManager; +import org.springframework.cloud.sleuth.instrument.TraceCallable; + +import com.netflix.hystrix.strategy.HystrixPlugins; +import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; + +public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { + + private final TraceManager traceManager; + + public SleuthHystrixConcurrencyStrategy(TraceManager traceManager) { + this.traceManager = traceManager; + HystrixPlugins.getInstance().registerConcurrencyStrategy(this); + } + + @Override + public Callable wrapCallable(Callable callable) { + return new TraceCallable<>(traceManager, callable); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConfiguration.java new file mode 100644 index 000000000..019e12cff --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConfiguration.java @@ -0,0 +1,19 @@ +package org.springframework.cloud.sleuth.instrument.hystrix; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.TraceManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.hystrix.HystrixCommand; + +@Configuration +@ConditionalOnClass(HystrixCommand.class) +@ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled", matchIfMissing = true) +public class SleuthHystrixConfiguration { + + @Bean SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(TraceManager traceManager) { + return new SleuthHystrixConcurrencyStrategy(traceManager); + } +} 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 96af97d05..a676845f5 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 @@ -5,6 +5,7 @@ org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.integration.TraceSpringIntegrationAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncCustomAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixConfiguration,\ org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\ diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/JavanicaITest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/JavanicaITest.java new file mode 100644 index 000000000..32789e6d8 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/JavanicaITest.java @@ -0,0 +1,125 @@ +package org.springframework.cloud.sleuth.instrument.hystrix; + +import static org.assertj.core.api.BDDAssertions.then; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.netflix.hystrix.EnableHystrix; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.TraceManager; +import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration; +import org.springframework.cloud.sleuth.trace.TraceContextHolder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.jayway.awaitility.Awaitility; +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = { + JavanicaITest.JavanicaITestConfiguration.class }) +public class JavanicaITest { + + @Autowired JavanicaClass javanicaClass; + @Autowired JavanicaDelegation javanicaDelegation; + @Autowired TraceManager traceManager; + + @Test + public void should_set_span_on_an_hystrix_command_annotated_method() { + final Span span = givenASpanInCurrentThread(); + + whenHystrixCommandGetsExecutedViaJavanica(); + + thenSpanPutInTheAsyncThreadIsSameAs(span); + } + + private Span givenASpanInCurrentThread() { + Span span = this.traceManager.startSpan("existing").getSpan(); + this.traceManager.continueSpan(span); + return span; + } + + private void whenHystrixCommandGetsExecutedViaJavanica() { + this.javanicaDelegation.doSthThatDelegatesToJavanica(); + } + + private void thenSpanPutInTheAsyncThreadIsSameAs(final Span span) { + Awaitility.await().until(new Runnable() { + @Override + public void run() { + then(span.getTraceId()).isNotNull() + .isEqualTo(javanicaClass.getTraceId()); + then(span.getName()) + .isNotEqualTo(javanicaClass.getSpanName()); + } + }); + } + + @After + public void cleanTrace() { + TraceContextHolder.removeCurrentTrace(); + } + + @DefaultTestAutoConfiguration + @EnableHystrix + @EnableAspectJAutoProxy(proxyTargetClass = true) + @Configuration + public static class JavanicaITestConfiguration { + + @Bean + JavanicaClass javanicaClass() { + return new JavanicaClass(); + } + + @Bean + JavanicaDelegation javanicaDelegation() { + return new JavanicaDelegation(javanicaClass()); + } + } + + public static class JavanicaDelegation { + + private final JavanicaClass javanicaClass; + + public JavanicaDelegation(JavanicaClass javanicaClass) { + this.javanicaClass = javanicaClass; + } + + public void doSthThatDelegatesToJavanica() { + this.javanicaClass.doSth(); + } + } + + public static class JavanicaClass { + + AtomicReference span; + + @HystrixCommand + public void doSth() { + this.span = new AtomicReference<>(TraceContextHolder.getCurrentSpan()); + } + + public String getTraceId() { + if (this.span == null || this.span.get() == null || (this.span.get() != null + && this.span.get().getTraceId() == null)) { + return null; + } + return this.span.get().getTraceId(); + } + + public String getSpanName() { + if (this.span == null + || (this.span.get() != null && this.span.get().getName() == null)) { + return null; + } + return this.span.get().getName(); + } + } +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncITest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncITest.java index dacbc5113..619b1697c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncITest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncITest.java @@ -25,15 +25,12 @@ import com.jayway.awaitility.Awaitility; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = { - TraceAsyncITest.CorrelationIdAsyncSpecConfiguration.class }) + TraceAsyncITest.TraceAsyncITestConfiguration.class }) public class TraceAsyncITest { - @Autowired - AsyncClass asyncClass; - @Autowired - AsyncDelegation asyncDelegation; - @Autowired - TraceManager traceManager; + @Autowired AsyncClass asyncClass; + @Autowired AsyncDelegation asyncDelegation; + @Autowired TraceManager traceManager; @Test public void should_set_span_on_an_async_annotated_method() { @@ -75,7 +72,7 @@ public class TraceAsyncITest { @EnableAsync @EnableAspectJAutoProxy(proxyTargetClass = true) @Configuration - public static class CorrelationIdAsyncSpecConfiguration { + public static class TraceAsyncITestConfiguration { @Bean AsyncClass asyncClass() {