By default Hystrix circuit breaker executes the method annotated with @HystrixCommand in a thread pool. That is why objects bound to ThreadLocal (tutorial) do no propagate to the @HystrixCommand . This limitation can be overcome by changing the default value of execution.isolation.strategy of THREAD to SEMAPHORE which causes the @HystrixCommand to be executed in the same caller thread (details).
The same thing applies if @SessionScope or @RequestScope are used. If you encounter a runtime exception that says it cannot find the scoped context, you need to change 'Isolation Strategy' to SEMAPHORE .
Example
Using Default Isolation Strategy
package com.logicbig.example;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class MyService {
@HystrixCommand(fallbackMethod = "defaultDoSomething")
public void doSomething() {
System.out.println("doSomething(): Thread: " + Thread.currentThread().getName());
MyObject myObject = MyThreadContext.getMyObject();
System.out.printf("MyObject: %s%n", myObject);
}
.............
public void defaultDoSomething(Throwable throwable) {
System.out.printf("Fallback method, exception=%s%n", throwable);
}
}
package com.logicbig.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@EnableCircuitBreaker
public class CircuitBreakerMain {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CircuitBreakerMain.class, args);
MyThreadContext.createMyObject("test1");
System.out.println("Main method. Thread: " + Thread.currentThread().getName());
MyService myService = ctx.getBean(MyService.class);
System.out.println("-- calling doSomething() --");
myService.doSomething();
}
}
Output
Main method. Thread: main
-- calling doSomething() --
doSomething(): Thread: hystrix-MyService-1
MyObject: null
Setting Isolation Strategy to SEMAPHORE
package com.logicbig.example;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class MyService {
.............
@HystrixCommand(fallbackMethod = "defaultDoSomething",
commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
})
public void doSomething2() {
System.out.println("doSomething2(): Thread: " + Thread.currentThread().getName());
MyObject myObject = MyThreadContext.getMyObject();
System.out.printf("MyObject: %s%n", myObject);
}
public void defaultDoSomething(Throwable throwable) {
System.out.printf("Fallback method, exception=%s%n", throwable);
}
}
package com.logicbig.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@EnableCircuitBreaker
public class CircuitBreakerMain2 {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CircuitBreakerMain2.class, args);
MyThreadContext.createMyObject("test2");
System.out.println("Main method. Thread: " + Thread.currentThread().getName());
MyService myService = ctx.getBean(MyService.class);
System.out.println("-- calling doSomething2() --");
myService.doSomething2();
}
}
Output
Main method. Thread: main
-- calling doSomething2() --
doSomething2(): Thread: main
MyObject: MyObject{name='test2'}
Example ProjectDependencies and Technologies Used: - Spring Boot 2.1.7.RELEASE
Corresponding Spring Version 5.1.9.RELEASE - Spring Cloud Greenwich.SR2
- spring-boot-starter : Core starter, including auto-configuration support, logging and YAML.
- spring-cloud-starter-netflix-hystrix 2.1.2.RELEASE: Spring Cloud Starter Netflix Hystrix.
- JDK 1.8
- Maven 3.5.4
|