Close

Spring Cloud - Hystrix CircuitBreaker, Thread Local Context Propagation

[Last Updated: Aug 11, 2020]

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 Project

Dependencies 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

Hystrix - Thread Local Context propagation Select All Download
  • circuit-breaker-thread-local-context
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyService.java

    See Also