Add passthrough mode to HystrixConcurrencyStrategy (#1433)

Add passthrough mode to HystrixConcurrencyStrategy. 

Fixes #1431.
This commit is contained in:
worldtiki
2019-08-27 08:56:09 +01:00
committed by Marcin Grzejszczak
parent aa1d0830ba
commit 33cab91195
6 changed files with 105 additions and 13 deletions

View File

@@ -1028,6 +1028,7 @@ That way, you ensure that a new span is created and closed for each execution.
We register a custom https://github.com/Netflix/Hystrix/wiki/Plugins#concurrencystrategy[`HystrixConcurrencyStrategy`] called `TraceCallable` that wraps all `Callable` instances in their Sleuth representative.
The strategy either starts or continues a span, depending on whether tracing was already going on before the Hystrix command was called.
Optionally, you can set `spring.sleuth.hystrix.strategy.passthrough` to `true` to just propagate the trace context to the Hystrix execution thread if you don't wish to start a new span.
To disable the custom Hystrix Concurrency Strategy, set the `spring.sleuth.hystrix.strategy.enabled` to `false`.
==== Manual Command setting

View File

@@ -23,6 +23,7 @@ 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.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -34,8 +35,8 @@ import org.springframework.context.annotation.Configuration;
* {@link com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy}.
*
* @author Marcin Grzejszczak
* @since 1.0.0
* @see SleuthHystrixConcurrencyStrategy
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(TraceAutoConfiguration.class)
@@ -43,12 +44,14 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(Tracing.class)
@ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled",
matchIfMissing = true)
@EnableConfigurationProperties(SleuthHystrixConcurrencyStrategyProperties.class)
public class SleuthHystrixAutoConfiguration {
@Bean
SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracing tracing,
SpanNamer spanNamer) {
return new SleuthHystrixConcurrencyStrategy(tracing, spanNamer);
SpanNamer spanNamer, SleuthHystrixConcurrencyStrategyProperties properties) {
return new SleuthHystrixConcurrencyStrategy(tracing, spanNamer,
properties.isPassthrough());
}
}

View File

@@ -60,9 +60,17 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
private HystrixConcurrencyStrategy delegate;
private boolean passthrough;
public SleuthHystrixConcurrencyStrategy(Tracing tracing, SpanNamer spanNamer) {
this(tracing, spanNamer, false);
}
public SleuthHystrixConcurrencyStrategy(Tracing tracing, SpanNamer spanNamer,
boolean passthrough) {
this.tracing = tracing;
this.spanNamer = spanNamer;
this.passthrough = passthrough;
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof SleuthHystrixConcurrencyStrategy) {
@@ -77,7 +85,7 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
.getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
.getPropertiesStrategy();
logCurrentStateOfHysrixPlugins(eventNotifier, metricsPublisher,
logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher,
propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
@@ -92,7 +100,7 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
}
}
private void logCurrentStateOfHysrixPlugins(HystrixEventNotifier eventNotifier,
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
@@ -109,13 +117,21 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
if (callable instanceof TraceCallable) {
return callable;
}
Callable<T> wrappedCallable = this.delegate != null
? this.delegate.wrapCallable(callable) : callable;
if (wrappedCallable instanceof TraceCallable) {
return wrappedCallable;
}
return new TraceCallable<>(this.tracing, this.spanNamer, wrappedCallable,
HYSTRIX_COMPONENT);
if (passthrough) {
return this.tracing.currentTraceContext().wrap(callable);
}
else {
return new TraceCallable<>(this.tracing, this.spanNamer, wrappedCallable,
HYSTRIX_COMPONENT);
}
}
@Override

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2013-2019 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
*
* https://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.hystrix;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth Hystrix settings.
*
* @author Daniel Albuquerque
*/
@ConfigurationProperties("spring.sleuth.hystrix.strategy")
public class SleuthHystrixConcurrencyStrategyProperties {
/**
* Enable custom HystrixConcurrencyStrategy that wraps all Callable instances into
* their Sleuth representative - the TraceCallable.
*/
private boolean enabled = true;
/**
* When enabled the tracing information is passed to the Hystrix execution threads but
* spans are not created for each execution.
*/
private boolean passthrough = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isPassthrough() {
return passthrough;
}
public void setPassthrough(boolean passthrough) {
this.passthrough = passthrough;
}
}

View File

@@ -24,12 +24,6 @@
"description": "Enable default AsyncConfigurer.",
"defaultValue": true
},
{
"name": "spring.sleuth.hystrix.strategy.enabled",
"type": "java.lang.Boolean",
"description": "Enable custom HystrixConcurrencyStrategy that wraps all Callable instances into their Sleuth representative - the TraceCallable.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.enabled",
"type": "java.lang.Boolean",

View File

@@ -21,8 +21,10 @@ import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import brave.Tracing;
import brave.propagation.CurrentTraceContext;
import brave.propagation.StrictScopeDecorator;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.propagation.TraceContext;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
@@ -123,6 +125,25 @@ public class SleuthHystrixConcurrencyStrategyTest {
then(this.reporter.getSpans()).hasSize(1);
}
@Test
public void should_propagate_trace_context_when_passthrough_is_enabled()
throws Exception {
SleuthHystrixConcurrencyStrategy strategy = new SleuthHystrixConcurrencyStrategy(
this.tracing, new DefaultSpanNamer(), true);
TraceContext traceContext = TraceContext.newBuilder().traceId(123L).spanId(456L)
.build();
CurrentTraceContext.Scope scope = tracing.currentTraceContext()
.newScope(traceContext);
Callable<TraceContext> callable = strategy
.wrapCallable(() -> tracing.currentTraceContext().get());
then(callable).isNotInstanceOf(TraceCallable.class);
then(callable.call()).isEqualTo(traceContext);
scope.close();
}
@Test
public void should_delegate_work_to_custom_hystrix_concurrency_strategy()
throws Exception {