Working with Bulkhead in Resilience4j

Reading Time: 3 minutes

Hi guys, In this blog, we will discuss a fault tolerance library which is, Resilience4j. So, this library comes into use when we want a system to continue operating properly in the event of the failure of some of its components. Let’s start with a quick introduction to Resilience4j.

Introduction

Resilience4j is a lightweight and easy-to-use fault tolerance library. It is inspired by Netflix Hystrix designed only for Java 8 and functional programming. It is Lightweight because the library only uses Vavr(a functional programming library) and does not have any other external library dependencies.
Resilience4j provides higher-order functions(or called decorators) to enhance any functional interface, lambda expression, or method reference with a Circuit Breaker, Rate Limiter, Retry, or Bulkhead. Now we will look at the modules of Resilience4j.

Resilience4j Modules

Resilience4j has several core modules and add-on modules from which you can choose all of the modules or any of them, that’s all up to you.

Core Modules(These are basic Modules in Resilience4j):

  • Circuit Breaker: Circuit breaking
  • Rate Limiter: Rate limiting
  • Bulkhead: Bulkheading
  • Retry: Automatic retrying (sync and async)
  • Cache: Result caching
  • Time Limiter: Timeout handling

Other Modules(It is the other modules apart from Core Modules):

  • Add-on modules
  • Framework modules
  • Reactive modules
  • Metrics modules
  • 3rd party modules

These are all the modules in Resilience4j but here we will talk about only the Bulkhead Module. We use the bulkhead module when we have to limit the number of concurrent execution. So, let’s start with what is Bulkhead.

What is Bulkhead

It ensures the failure in one service doesn’t cause the whole system to go down. It means do not burden service with calls more than its capacity and for that it controls the number of concurrent requests the service can take, the number of resources waiting for the response from the service can be limited by this way. There are two implementations of bulkhead patterns in Resilience4j.

  • Semaphore – In this approach, we limit the number of concurrent requests to the service. It will reject the incoming requests once the limit is hit.
  • FixedThreadPoolBulkhead – In this approach, we isolate a set of thread pools from system resources, using only that thread pool for the service. We also use a waiting queue apart from the thread pool, if both the thread pool and queue are full then in that case, the request will get rejected with BulkheadFullException.

Steps to write code

Step-1. Add the custom configuration of the bulkhead according to use-case in the application.yaml.

resilience4j.bulkhead:
  instances:
    bulkheadService1:
      maxWaitDuration: 1000ms
      maxConcurrentCall: 2

resilience4j.thread-pool-bulkhead:
  instances:
    bulkheadService1:
      maxThreadPoolSize: 1
      coreThreadPoolSize: 1
      queueCapacity: 1

Here maxConcurrentCalls is the max amount of parallel execution allowed by the bulkhead and maxWaitDuration is the max amount of time a thread will be blocked for attempting to enter the maxConcurrentCalls count.

Step-2. Create a controller class which will have the following endpoint.
Here, for demo the behaviour of bulkhead, will hit the target endpoint 100 times.

@RestController
public class UserRegistrationController {
@PostMapping("/register/seller")
    public String registerAsSeller(@RequestBody SellerDto sellerDto) throws InterruptedException {
        String registerSeller = null;
        for (int i = 0; i < 100; i++) {
            long start = System.currentTimeMillis();
            new Thread(() -> {
                try {
                    userRegistrationResilience4j.registerSeller(sellerDto);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "service-call").start();
            Thread.sleep(50);
        }

        return registerSeller;

    }
}

Here service Implementation is wrapped with @Bulkhead annotation. This annotation supports fallbackMethod attribute and redirects the call to the fallback functions in case of failures observed by the pattern. We would also need to define the implementation of the fallback method.


@Service
public class UserRegistrationResilience4j {
    @Bulkhead(name = "bulkheadService1", fallbackMethod = "bulkHeadFallback")
    public String registerSeller(SellerDto sellerDto) throws InterruptedException {
        String response = restTemplate.postForObject("/addSeller", sellerDto, String.class);
        return response;
    }
    
    public String bulkHeadFallback(SellerDto sellerDto, Throwable t) {
        logger.error("Inside bulkHeadFallback, cause - {}", t.toString());
        return "Inside bulkHeadFallback method. Some error occurred while calling service for seller registration";
    }
    
}

After execution, we can observe that the target service has overloaded after some successful calls and some threads got rejected after maxWaitDuration.

That’s pretty much it from the article. If you have any feedback or queries, please do let me know in the comments. Also, if you liked the article, please give me a thumbs up and I will keep writing blogs like this for you in the future as well. Keep reading and Keep coding.

Written by 

I am a genius middle-class noble philanthropist(by heart) . & Simply a technology enthusiast who loves to share and gain knowledge through blogs.

2 thoughts on “Working with Bulkhead in Resilience4j4 min read

    1. you can add other configurations too as per your use.
      Like:- maxConcurrentCalls, maxWaitDuration, writableStackTraceEnabled, fairCallHandlingEnabled

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading