In the previous tutorial, we created three core microservices for our simple shopping cart project.
In this tutorial, we will learn how to use the Spring Cloud OpenFeign library to make REST API calls (Synchronous communication) between multiple microservices.Spring Cloud Open Feign Overview
Feign is a declarative web service client. It makes writing web service clients easier.
To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations.
Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web.
Spring Cloud integrates Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced HTTP client when using Feign.
What we will Build?
We will use Spring Cloud OpenFeign to make REST API calls from order-service to product-service and payment-service.
Prerequisites
Step 1: Add Spring cloud open feign Maven dependency to order-service
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Make sure to add spring cloud dependencies and their version.
Here is the complete pom.xml file after adding Spring cloud open feign dependency:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.microservice</groupId>
<artifactId>orderservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>orderService</name>
<description>Order Service for Microservice</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
<jwt.version>0.9.1</jwt.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.microservice</groupId>
<artifactId>productservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 2: Enable Feign Client using @EnableFeignClients
package com.microservice.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
Note that @EnableFeignClients annotation enables component scanning for interfaces that declare they are Feign clients.
Step 3: Create Feign REST Client to Call Product and Payment Services
ProductService Feign Client
Let's create an interface named ProductService and add the following code:
package com.microservice.orderservice.external.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "PRODUCT-SERVICE", url = "http://localhost:8081/product")
public interface ProductService {
@PutMapping("/reduceQuantity/{id}")
ResponseEntity<Void> reduceQuantity(
@PathVariable("id") long productId,
@RequestParam long quantity
);
}
We declare a Feign client using the @FeignClient annotation:
@FeignClient(name = "PRODUCT-SERVICE", url = "http://localhost:8081/product")
The name argument passed in the @FeignClient annotation is a mandatory, arbitrary client name, while with the URL argument, we specify the API base URL.
@FeignClient(name = "PRODUCT-SERVICE", url = "http://localhost:8081/product")
Furthermore, since this interface is a Feign client, we can use the Spring Web annotations to declare the APIs that we want to reach out to.
PaymentService Feign Client
package com.microservice.orderservice.external.client;
import com.microservice.orderservice.exception.OrderServiceCustomException;
import com.microservice.orderservice.payload.request.PaymentRequest;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = "PAYMENT-SERVICE", url = "http://localhost:8083/payment")
public interface PaymentService {
@PostMapping
public ResponseEntity<Long> doPayment(@RequestBody PaymentRequest paymentRequest);
}
Step 4: Change the placeOrder method to Use Feign Client
First, inject both the Feign clients:
private final ProductService productService;
private final PaymentService paymentService;
Next, use its methods:
// ....
productService.reduceQuantity(orderRequest.getProductId(), orderRequest.getQuantity());
// ...
paymentService.doPayment(paymentRequest);
// ...
Here is the complete code of placeOrder() method in OrderServiceImpl class:
@Override
public long placeOrder(OrderRequest orderRequest) {
log.info("OrderServiceImpl | placeOrder is called");
//Order Entity -> Save the data with Status Order Created
//Product Service - Block Products (Reduce the Quantity)
//Payment Service -> Payments -> Success-> COMPLETE, Else
//CANCELLED
log.info("OrderServiceImpl | placeOrder | Placing Order Request orderRequest : " + orderRequest.toString());
log.info("OrderServiceImpl | placeOrder | Calling productService through FeignClient");
productService.reduceQuantity(orderRequest.getProductId(), orderRequest.getQuantity());
log.info("OrderServiceImpl | placeOrder | Creating Order with Status CREATED");
Order order = Order.builder()
.amount(orderRequest.getTotalAmount())
.orderStatus("CREATED")
.productId(orderRequest.getProductId())
.orderDate(Instant.now())
.quantity(orderRequest.getQuantity())
.build();
order = orderRepository.save(order);
log.info("OrderServiceImpl | placeOrder | Calling Payment Service to complete the payment");
PaymentRequest paymentRequest
= PaymentRequest.builder()
.orderId(order.getId())
.paymentMode(orderRequest.getPaymentMode())
.amount(orderRequest.getTotalAmount())
.build();
String orderStatus = null;
try {
paymentService.doPayment(paymentRequest);
log.info("OrderServiceImpl | placeOrder | Payment done Successfully. Changing the Oder status to PLACED");
orderStatus = "PLACED";
} catch (Exception e) {
log.error("OrderServiceImpl | placeOrder | Error occurred in payment. Changing order status to PAYMENT_FAILED");
orderStatus = "PAYMENT_FAILED";
}
order.setOrderStatus(orderStatus);
orderRepository.save(order);
log.info("OrderServiceImpl | placeOrder | Order Places successfully with Order Id: {}", order.getId());
return order.getId();
}
Comments
Post a Comment
Leave Comment