0% found this document useful (0 votes)
20 views7 pages

BulkHead Pattern

Uploaded by

Amr Mohamed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views7 pages

BulkHead Pattern

Uploaded by

Amr Mohamed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Why bulkhead pattern?

A ship is split into small multiple compartments using Bulkheads. Bulkheads are used to seal parts of the
ship to prevent the entire ship from sinking in case of a flood. Similarly, failures should be expected when
we design software. The application should be split into multiple components and resources should be
isolated in such a way that failure of one component is not affecting the other.

For ex: let's assume that there are 2 services A and B. Some of the APIs of A depends on B. For some
reason, B is very slow. So, when we get multiple concurrent requests to A which depends on B, A’s
performance will also get affected. It could block A’s threads. Due to that A might not be able to serve
other requests which do NOT depend on B. So, the idea here is to isolate resources / allocate some
threads in A for B. So that We do not consume all the threads of A and prevent A from hanging for all the
requests!

Bulkhead pattern:
The Bulkhead pattern is a design pattern used to improve the resilience and fault tolerance of a
distributed system by isolating different parts of the system from each other. By creating separate
compartments for different resources or services, the pattern can help prevent cascading failures and
limit the impact of failures on the rest of the system.

The Bulkhead pattern can be used in combination with other resilience patterns, such as the Circuit
Breaker pattern, Retry pattern, and Timeout pattern, to create a more resilient and fault-tolerant system.

Let’s try bulkhead pattern:


We need to build two service:

1. A normal microservice

2. A normal microservice with a bulkhead communicates with first one.

The steps to build a microservice with circuit breaker pattern(ShopMicroservice):

1. We can use https://start.spring.io/

Dependency you may need:

i. Lombok

ii. Resilience4j

iii. spring-boot dev-tools

iv. spring web

2. application.yml for service that use other service:


resilience4j.bulkhead:
instances:
orderService:
maxWaitDuration: 3000 #wait duaration
maxConcurrentCalls: 7
maxWaitDuartion→ The maximum time in milliseconds that a thread should wait to acquire a
permit to execute a guarded call. If the maximum wait time is exceeded, the call will fail with
a BulkheadFullException.

maxConcurrentCalls→ The maximum number of concurrent calls that are allowed to execute at
the same time. If the maximum number of concurrent calls is exceeded, additional calls will be
rejected with a BulkheadFullException.

To use this configuration in your Java code, you can create a Bulkhead instance using
the BulkheadRegistry and BulkheadConfig classes from the Resilience4j library, like this:

BulkheadConfig config = BulkheadConfig.custom()

.maxWaitDuration(Duration.ofMillis(3000))

.maxConcurrentCalls(7)

.build();

BulkheadRegistry registry = BulkheadRegistry.of(config);

Bulkhead bulkhead = registry.bulkhead("orderService");

3. We will write bulkhead pattern like circuitbreaker:


@Bulkhead(name=ORDER_SERVICE,fallbackMethod = "bulkHeadFallback")

Fallbackmethod called when concurrent call exceeds the limit of time and number of calls.

The controller class for this service will be on localhost:8080:[Shop Service]


package com.example.resilience4j.springresilience4jbulkhead.controller;

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.time.LocalTime;

@RestController
public class ShopControllerwithBulkhead {

private static final Logger logger =


LoggerFactory.getLogger(OrderControllerwithBulkhead.class);
private static final String ORDER_SERVICE ="orderService" ;

@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
private int attempts=0;

@GetMapping("/order")
@Bulkhead(name=ORDER_SERVICE,fallbackMethod = "bulkHeadFallback")
public ResponseEntity<String> createOrder()
{
String response =
restTemplate.getForObject("http://localhost:9191/orders",
String.class);
logger.info(LocalTime.now() + " Call processing finished = " +
Thread.currentThread().getName());
return new ResponseEntity<String>(response, HttpStatus.OK);
}

public ResponseEntity<String> bulkHeadFallback(Exception t)


{
return new ResponseEntity<String>(" orderService is full and
does not permit further calls", HttpStatus.TOO_MANY_REQUESTS);
}

4. On localhost:9191→Other service is a normal order service returns all orders and sleep for a
period of time.[Order Service]
package com.javatechie.os;

import com.javatechie.os.entity.Order;
import com.javatechie.os.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@SpringBootApplication
@RestController
@RequestMapping("/orders")
public class OrderServiceApplication {
@Autowired
private OrderRepository orderRepository;

@PostConstruct
public void initOrdersTable() {
orderRepository.saveAll(Stream.of(
new Order("mobile", "electronics", "white",
20000),
new Order("T-Shirt", "clothes", "black", 999),
new Order("Jeans", "clothes", "blue", 1999),
new Order("Laptop", "electronics", "gray", 50000),
new Order("digital watch", "electronics", "black",
2500),
new Order("Fan", "electronics", "black", 50000)
).
collect(Collectors.toList()));
}

@GetMapping
public List<Order> getOrders(){

try{
Thread.sleep(7000); //we will try with 7000 and 2000
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Worked");
return orderRepository.findAll();
}
@GetMapping("/{category}")
public List<Order> getOrdersByCategory(@PathVariable String category){
return orderRepository.findByCategory(category);
}

public static void main(String[] args) {


SpringApplication.run(OrderServiceApplication.class, args);
}

5. Other class call shop service with number of concurrent calls:


package com.example.resilience4j.springresilience4jbulkhead.controller;

import
com.example.resilience4j.springresilience4jbulkhead.SpringResilience4jB
ulkheadApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.web.client.RestTemplate;

import java.util.stream.IntStream;

public class CallinConcurrent {


public static void main(String[] args) {

int i=1;
IntStream.range(i,7).parallel().forEach(t->{
String response = new
RestTemplate().getForObject("http://localhost:8080/order",
String.class);

System.out.println(response);

});
}
}

Then we go to the test of bulkhead pattern:


1.First , we will go for normal case and set the thread sleep in order service with 2000ms.

We will get this result when run CallinConcurrent class with (concurrent calls = 5) =<
(maxConcurrentCall=7) → five calls are succeeded

On Shop service console:

2. Second, we will go for normal case and set the thread sleep in order service with 2000ms.

We will get this result when run CallinConcurrent class with (concurrent calls = 20) >
(maxConcurrentCall=7) → 20 calls are succeeded
3.Third, we will go for not-normal case and set the thread sleep in order service with 7000ms.

We will get this result when run CallinConcurrent class with (concurrent calls = 5) <
(maxConcurrentCall=7) → 5 calls are succeeded because time and number of calls did not affect or
exceed the bulkhead configuration.

4.Fourth, we will go for not-normal case and set the thread sleep in order service with 7000ms.

We will get this result when run CallinConcurrent class with (concurrent calls = 20) >
(maxConcurrentCall=7) →

5.Fifth, we will go for not-normal case and set the thread sleep in order service with 7000ms.

We will get this result when run CallinConcurrent class with (concurrent calls = 500) >
(maxConcurrentCall=7) → we will get the same results and if we browse localhost:8080\order
we can see this result:
After 500 requests are done, we refresh the page and we will get:

You can use bulkhead pattern with different services by write the bulkhead
configuration for each service and relate it to @Bulkhead and service a name
@Bulkhead(name=service name→,fallbackMethod = bulkHeadFallbackname
for this service→)

resilience4j.bulkhead:
instances:
Servicename→:
maxWaitDuration: 3000 #wait duaration
maxConcurrentCalls: 7

References:
1. https://dzone.com/articles/resilient-microservices-pattern-bulkhead-pattern
2. https://www.vinsguru.com/bulkhead-pattern/
3. https://github.com/shameed1910/spring-resilience-bulkhead
4. https://www.youtube.com/watch?v=VjmUW6H3GCM

You might also like