Using Netflix Feign Client for Service Calls
First, add the spring-cloud-starter-openfeign dependency to your project:
<?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 http://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.1.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>service-consumer</artifactId>
<name>service-consumer</name>
<description>Service consumer application</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>shared-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Other dependencies -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</plugin>
</plugins>
</build>
</project>
Next, define a FeignClient interfacce for the service you want to call. Note that you must specify the service name in the value attribute:
package com.example.client.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.shared.model.ApiResponse;
@FeignClient(value = "service-provider")
@RequestMapping("/api/service")
public interface ProviderServiceClient {
@GetMapping("/details")
ApiResponse<String> getServiceDetails();
}
Implement the service endpoint:
package com.example.provider.controller;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RestController;
import com.example.shared.model.ApiResponse;
import com.example.client.feign.ProviderServiceClient;
@RestController
public class ProviderController implements ProviderServiceClient {
@Override
public ApiResponse<String> getServiceDetails() {
ApiResponse<String> response = new ApiResponse<>();
response.setStatus(String.valueOf(HttpStatus.OK.value()));
response.setMessage(HttpStatus.OK.getReasonPhrase());
response.setData("Service Provider Details");
return response;
}
}
For the API module, include the 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 http://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.1.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>shared-api</artifactId>
<name>shared-api</name>
<description>Shared API models</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<!-- Spring Cloud Feign dependency -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Other dependencies -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</project>
In the consumer application's main class, enable Feign clients and specify the packages to scan:
package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.client.feign.ProviderServiceClient;
import com.example.shared.model.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients({"com.example.client.feign"})
@RestController
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Autowired
private ProviderServiceClient providerServiceClient;
@GetMapping("/call-service")
public ApiResponse<String> callService() {
return providerServiceClient.getServiceDetails();
}
}
Using RestTemplate with Ribbon for Service Calls
Create a RestTemplate bean with the @LoadBalanced annotation to anable Ribbon load balancing:
@LoadBalanced
@Bean
public RestTemplate loadBalancedRestTemplate() {
return new RestTemplate();
}
Use the load-balanced RestTemplate to call services:
@GetMapping("/ribbon-call")
public ApiResponse<String> ribbonCall() {
ResponseEntity<ApiResponse> response = restTemplate.exchange(
"http://service-provider/api/service/details",
HttpMethod.GET, null, ApiResponse.class);
return response.getBody();
}
Using DiscoveryClient with Standard RestTemplate
Use DiscoveryClient to locate service insatnces and then call them with a standard RestTemplate:
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/discovery-call")
public ApiResponse<String> discoveryCall() {
List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
if (instances.isEmpty()) {
ApiResponse<String> errorResponse = new ApiResponse<>();
errorResponse.setStatus("404");
errorResponse.setMessage("Service not found");
return errorResponse;
} else {
ServiceInstance instance = instances.get(0);
String serviceUrl = instance.getUri().toString();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<ApiResponse> response = restTemplate.exchange(
serviceUrl + "/api/service/details",
HttpMethod.GET, null, ApiResponse.class);
return response.getBody();
}
}