Stub getting lost while using hystrix
See original GitHub issueHi all, I’d like to report not a bug but a limitation i have encounter.
I created a test that use Wiremock rule :
@Rule
public WireMockRule myRemoteService = new WireMockRule(4242);
// ...
myRemoteService.stubFor(//
get("/entities")//
.willReturn(//
aResponse()//
.withStatus(HttpStatus.SC_OK)//
.withHeader("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE)//
.withBody("[]")));
This remote service is accessed through a feign client with ribbon and hystrix in the mix.
If hystrix is not enabled, everything works fine. As soon as i enable hystrix, the stub is not used and i got a hystrix exception (and no fallback are defined). Deeping in hystrix code to debug before the request execution, I found that wiremock mapping are not defined. This is because hystrix execute in a separate thread than the test’s one.
To fix the problem, 2 solutions :
- make hystrix work in the same thread (isolation strategy : semaphore) [theory, i didn’t test this approach]
- write your own concurrency strategy (get and store the wiremock threadlocal value before going into separate thread, then, in the separate thread, put back the wiremock value) [i had it working in our project]
Here is the strategy i wrote (only for the test) :
import java.util.concurrent.Callable;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
@Component
@Primary
public class TestHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
public TestHystrixConcurrencyStrategy() {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
} catch (IllegalStateException ise) {
log.info("strategy already exists", ise);
HystrixConcurrencyStrategy existingStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
log.debug("here is the existing strategy", existingStrategy);
}
}
@SuppressWarnings("unchecked")
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
ThreadLocal<WireMock> wiremockTL;
try {
wiremockTL = (ThreadLocal<WireMock>) FieldUtils.readStaticField(WireMock.class, "defaultInstance", true);
} catch (IllegalAccessException exception) {
throw new TechnicalException("Failure to access Wiremock.defaultInstance", exception);
}
return new HystrixContextWrapper<>(callable, wiremockTL);
}
public static class HystrixContextWrapper<V> implements Callable<V> {
private Callable<V> delegate;
private WireMock wireMock;
private ThreadLocal<WireMock> wiremockTL;
/**
* Wrap a callable.
*
* @param delegate the callable to wrap
*/
public HystrixContextWrapper(Callable<V> delegate, ThreadLocal<WireMock> wiremockTL) {
this.delegate = delegate;
this.wiremockTL = wiremockTL;
wireMock = wiremockTL.get();
}
@Override
public V call() throws Exception {
WireMock wireMockInitial = wiremockTL.get();
wiremockTL.set(wireMock);
try {
return this.delegate.call();
} finally {
wiremockTL.set(wireMockInitial);
}
}
}
}
I’m not satisfied with this approach because i’m depending on static attribute’s name and i have to do reflection to access it. For test purpose, it’s ok i guess. I’d like to know if there is a better way to define wiremock rule in a multithread context like this (and i’d love to keep the simplicity of the wiremockrule =P).
Issue Analytics
- State:
- Created 7 years ago
- Comments:9
I found the same problem when i wrote some test for Hystrix timeout.
With Hystrix enable and using the @Rule the server get lost and not respond some times.
The workaround was to use the WireMockServer instead of WireMockRule
It is about hystrix default timeout. Because stub may appear after hystrix timeout passed. You should increase it as @devjiel mentioned for all hystrix timeouts with this property:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 10000