Bulkhead: Fault-Tolerant Microservice (Part-2)
Wednesday, 16 July 2025 5:50 AM
To build Fault tolerant microservices: Resilience4j provides below
mechanisms
(already covered in Part-1)
Rate Limiter
Retry Bulkhead
Resilience4j
Circuit Breaker Time Limiter
Lets see in depth of:
Bulkhead:
Rate Limiter:
• Protects our application from our clients by limiting how many requests we accept within a
specific time window from them.
• also Rate limiter never talks about the concurrency.
Ex: let say our application is "Order".
Req at 1st sec
Client Req at 2nd sec Order
Req at 3rd sec
Req at 4th sec
Product
Req at 5th sec
Too many
requests, request
limit is 4 in 5sec.
Bulkhead:
• It helps to control how many concurrent requests can go to downstream service.
Ex: let say our application is "Order"
Use-Case: Let say, "Product" is small and light weight service and can only handle 3 concurrent
request.
(3 calls at same time)
Client Order
Product
Only 3 concurrent request is allowed, 4th
request will be either blocked or rejected
until one of the previous ones completes.
Above use case is resolved through "Semaphore
Bulkhead"
• It also protects our application from our downstream services by limiting how many threads
we allocate to them.
Use-Case: Let say our Order Service has 2 APIs (endpoints)
API 1 : make a downstream call to Product service.
API 2 : make a downstream call to Payment service.
Product service takes ~100 milliseconds to process a request (Fast Service)
and
Payment service takes ~5second to process a request (Slow service)
What if there is a sudden spike in API 2 traffic and more and more calls are made to Payment service (say calls are not
concurrent).
Since Payment service is slow, it holds or blocks the thread longer( for ~5seconds) and its possible that all the threads of Order
service now blocked (by Payment service only).
Now even the Order service fast endpoint (API 1) becomes unavailable as there is no threads available.
Order
Product (fast service)
Client API1
No more
threads
available
API2 Payment (slow service)
(all threads blocked because of slow service)
Above use case is resolved through "Thread Pool
Bulkhead"
Bulkhead
Semaphore Bulkhead Thread Pool Bulkhead
Semaphore Bulkhead:
• Limits the number of concurrent calls using
counter.
• If the limit is reached, further calls are
rejected immediately or blocked for specific
wait time.
it limits the number of concurrency call, via Semaphore Lock.
I have already explained in depth about Semaphores in
Multithreading topic of Java playlist. Have a look if there is
any doubt with Semaphore locks.
[Link] dependency
[Link]
Accepted values for maxWaitDuration: It accepts values in the following format:
<number><time-unit>
Supported time units:
• ms — milliseconds
• s — seconds
• m — minutes
• h — hours
• d — days (not typical, but valid)
Example:
[Link]=0 # Reject immediately
[Link]=300ms # Wait 300 milliseconds
[Link]=2s # Wait 2 seconds
[Link]=1m # Wait 1 minute
Thread Pool Bulkhead:
• Assign a dedicated thread pool for each service.
• Only that pool is used to call the downstream.
If you have any doubts, how Thread Pools and Thread Pool Executor works, kindly have a look at this video in
Java playlist, I have already discussed in depth.
• The AOP proxy intercepts your method call.
• It submits your method to the Bulkhead's thread pool (configured via [Link]).
[Link](() -> {
return ourMethodLogic(); // The whole method body runs here
}, bulkheadThreadPoolExecutor);
• It returns a CompletableFuture to the caller for async response.
• Inside your method, [Link](...) just wraps the result in a completed future — it does
NOT run anything in a thread pool.
[Link]
ThreadPool for productService will be created with these configurations.
Testing the Output:
Just added [Link] for testing purpose.
Output:
Max thread pool size is = 3 and Queue size is = 2
Invoked the API 6 times, lets see the output:
Thread1, RQ1
Thread2, RQ2
Thread3, RQ3 and RQ4 and RQ5 is inserted into the queue
RQ6 is rejected, as there is no thread available and no space in queue
Thread1 becomes free and picked RQ4 from queue
Thread2 becomes free and picked RQ5 from queue
Time Limiter:
• Time Limiter is used to prevent async call from hanging indefinitely.
• Time Limiter is non blocking in Resilience4j.
• Means, it is mainly designed for asynchronous operations, that returns a reactive type like
Mono, Flux etc.
But currently we have covered Blocking calls like RestTemplate, RestClient and
FeignClient.
And we have already covered how to control the time out of the blocking calls.
Feign Client example:
Time Limiter context is same but for reactive calls. Will cover TimeLimiter, when will start Spring Web
flux (reactive programming)