1

Resilience4j version: 1.7.0

Java version: 1.8

I have challenge in implementing TimeLimiter feature of Resilience4j. I am able to get the Circuit Breaker (CB) work.

We have 2 services Lets say serviceA and serviceB. We have used Command design pattern which encapsulates logic to communicate with ServiceB. RabbitMQ is used to establish inter microservice communication. We had implemented Hystrix CB by making all our Command classes extend HystrixCommand. When we decided to move to Resilience4j main challenge was to retain the existing design pattern than configuring Resilence4J CB.

We have Synchronous communication at present between ServiceA and ServiceB. Though we use RabbitMQ to communicate which is Async communication, with the help of Spring wrapper method RabbitTemplate.convertSendAndReceive() we are able to achieve Sync mode of communication with RabbitMQ.

When I removed HystrixCommand reference which was the Base class for all my Command classes, naturally there was a need to implement a custom Base Command class which will be implemented using Resilience4J Decorators.

I managed introduce a Resilience4JCommand abstract class which will implement a execute() and execute run() from all my command classes. Also defined a abstract run() which all my existing Command classes will override and implement business logic. I understood from many of the discussion that our method which needs to implement CB pattern needs to return of type CompletableFuture and also understood from many places that fallback method also must have same return type. My Base Command Class Resilience4JCommand looks something like below


import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import com.ge.hc.XYZ.exception.ResourceNotFoundException;

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.bulkhead.annotation.Bulkhead.Type;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;

@Component
public abstract class Resilience4JCommand<R> {

    /** The class logger. */
    protected static final Logger LOGGER = LoggerFactory.getLogger(Resilience4JCommand.class);

    public R execute() {
        R result = null;
        try {
            result = executeWithCircuitBreaker().get();
        } catch (Exception e) {
            System.out.println("Inside Catch block of executeAsync ...........**************\n\n ");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        
        return result;
    }
    
    @Bulkhead(name = "XYZStreamingServer3", fallbackMethod = "getFallback", type = Bulkhead.Type.THREADPOOL)
    @TimeLimiter(name = "XYZStreamingServer2", fallbackMethod = "getFallback")
    @CircuitBreaker(name = "XYZStreamingServer1", fallbackMethod = "getFallback")
    public CompletableFuture<R> executeWithCircuitBreaker() {
        return CompletableFuture.supplyAsync(new Supplier<R>() {
            @Override
            public R get() {
                    return run();
            }
        });
    }


    protected abstract R run();
    
   
    public CompletableFuture<R> getFallback(Throwable e) {
      StringWriter sw = new StringWriter();
      PrintWriter pw = new PrintWriter(sw);
      
        if (e != null) {
            e.printStackTrace(pw);
        }

      String reason = sw.toString();

      LOGGER.error("Calling XYZ-hystrix fallback method for command: {}; fallback reason: {}",
              this.getClass().getSimpleName(), (reason.isEmpty() ? "unknown" : reason));
      throw new ResourceNotFoundException("Circuit Breaker ");
  }
}

But nothing works with above setup. I am able to achieve CB alone work without the need of writing new method executeWithCircuitBreaker() which returns CompletableFuture. I can make CB work just with below execute()

Bulkhead AND TimeLimiter do not work with return type other than CompletableFuture

@CircuitBreaker(name = SCHEME_NAME, fallbackMethod = "getFallback")
    public R execute() {
        return run();
    }

I have spent more than a week in setting up this .. Helpful if someone can point me what I am missing

My application.properties looks something like belwo

management.health.circuitbreakers.enabled=true
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=always

resilience4j.circuitbreaker.instances.XYZStreamingServer1.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.eventConsumerBufferSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.failureRateThreshold=50
resilience4j.circuitbreaker.instances.XYZStreamingServer1.minimumNumberOfCalls=5
resilience4j.circuitbreaker.instances.XYZStreamingServer1.automaticTransitionFromOpenToHalfOpenEnabled=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.waitDurationInOpenState=5s
resilience4j.circuitbreaker.instances.XYZStreamingServer1.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowType=COUNT_BASED

resilience4j.timelimiter.instances.XYZStreamingServer2.timeoutDuration=5s
resilience4j.timelimiter.instances.XYZStreamingServer2.cancelRunningFuture=true

resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.maxThreadPoolSize=10
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.coreThreadPoolSize=5
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.queueCapacity=5
Vinay
  • 11
  • 3
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community May 28 '22 at 01:04
  • Just to update the thread for later reference .. Issue was with usage of CompletableFuture.supplyAsync along with BulkHead. Since BulkThread already spawns a thread for my method execution , using CompletableFuture.supplyAsync was unnecessary. And that was causing Timeout to not work. – Vinay May 31 '22 at 20:04

0 Answers0