Skip to main content

Handling Concurrent Service Calls in a Spring Boot Application: CompletableFuture and @Async

In modern e-commerce applications, service endpoints often need to consolidate data from multiple sources. Imagine an API to retrieve an order summary, gathering details from services like order-service, customer-service, and inventory-service, along with a local method call for payment details.

Efficient handling of these concurrent calls is crucial for performance, and Spring Boot offers two primary approaches: asynchronous programming using CompletableFuture (for Spring MVC) and reactive programming using WebClient (for Spring WebFlux).

Here, we explore CompletableFuture approach, highlighting the advantages and implementations in a Spring Boot e-commerce context.

Asynchronous Programming with CompletableFuture and @Async in Spring Web

This approach leverages the @Async annotation and ompletableFuture to make asynchronous calls to the order-service, customer-service, and inventory-service in a Spring Web application.

1) Enable Async Support

First, ensure that your Spring Boot application is configured to support asynchronous processing by adding @EnableAsync in the main application class.

2) Create Client classes

In these service classes, we will make asynchronous HTTP calls to the order-service, customer-service, and inventory-service.


3) Internal method call for Payment Info

This PaymentService demonstrates an internal call to calculate payment logic. It introduces a 500-millisecond delay to simulate the time it might take to fetch or calculate payment data from an external system or database.

4) Create a order aggregation service class

This service will call the asynchronous service clients and consolidate the results.

5) Create a order aggregation controller class

In your controller, call aggregateOrderData and return the result.

6) Add external services URLs in application.properties

These are the external service URLs to be used in the respective client to call endpoints.


7) Create "order-service", "customer-service", and "inventory-service"

These are really simple Spring Boot services with only one endpoint, so there is no point in discussing them in detail. Please find below the repo link for them.

1) The order-service

Repo link: GitHub

Swagger: http://localhost:8081/swagger-ui.html

2) The customer-service

Repo link: GitHub

Swagger: http://localhost:8082/swagger-ui.html

3) The inventory-service

Repo link: GitHub

Swagger: http://localhost:8083/swagger-ui.html

Each service has one endpoint that simulates a delay by using Thread.sleep().

8) Test the application

Run your Spring Boot application and navigate to http://localhost:8080/swagger-ui/ to access the Swagger UI. This will allow you to test your endpoints interactively.

2024-11-11T18:18:14.343+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [nio-8080-exec-5] com.cb.controller.OrderController : Fetching order summary for order id: 560ac632-bee9-4322-b204-c4f77e0e2adc, customer id: 77f67c24-19af-4f85-b9d1-561a2afe47e7, product id: 4eea9612-0c17-4ce3-9fd7-689d7b9c01b9
2024-11-11T18:18:14.359+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-1] com.cb.client.order.OrderClient : Fetching order from order service for order id: 560ac632-bee9-4322-b204-c4f77e0e2adc
2024-11-11T18:18:14.359+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-2] com.cb.client.customer.CustomerClient : Fetching customer from customer service for customer id: 77f67c24-19af-4f85-b9d1-561a2afe47e7
2024-11-11T18:18:14.359+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-3] c.cb.client.inventory.InventoryClient : Fetching inventory from inventory service for product id: 4eea9612-0c17-4ce3-9fd7-689d7b9c01b9
2024-11-11T18:18:14.931+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-3] c.cb.client.inventory.InventoryClient : Inventory fetched successfully for product id: 4eea9612-0c17-4ce3-9fd7-689d7b9c01b9
2024-11-11T18:18:14.931+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-2] com.cb.client.customer.CustomerClient : Customer fetched successfully for customer id: 77f67c24-19af-4f85-b9d1-561a2afe47e7
2024-11-11T18:18:14.931+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-1] com.cb.client.order.OrderClient : Order fetched successfully for order id: 560ac632-bee9-4322-b204-c4f77e0e2adc
2024-11-11T18:18:14.932+05:30 INFO 6942 --- [concurrent-service-calls-spring-boot-completable-future] [ task-1] com.cb.controller.OrderController : Order summary fetched successfully for order id: 560ac632-bee9-4322-b204-c4f77e0e2adc, customer id: 77f67c24-19af-4f85-b9d1-561a2afe47e7, product id: 4eea9612-0c17-4ce3-9fd7-689d7b9c01b9 in 590 ms

From the logs, you can see that the entire process took approximately 600 ms. Without parallel calls, it would have taken more than 2000 ms, with about 500 ms for each external service call as well as one internal method call.

Source code: GitHub

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...