Spring Cloud – Feign clients

In this post we will see, how you can write declarative REST clients with Feign. Till now we have created microservices and registered with Eureka. We called these services from othr java services using ribbon which is a client side load balancer. RestTemplate is also ribbon aware and we used that too.

Feign is a declarative way to write REST client, i.e we can write calls to REST services with no implementation code. Its an alternative to RestTemplate and in a way easier than it. Spring cloud provides easy to use wrapper around Feign.

Lets start by taking an example. Here we have a Eureka server and a user-service regitered to it.

spring-feign2

This user-service is a simple REST webserive with following mappings:


import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserServiceRestController {

@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
@ResponseBody
public String getUserDetails(@PathVariable("id") int id) {

return "User details for id " +id;
}

@RequestMapping(value = "/getAllGroups", method = RequestMethod.GET)
@ResponseBody
public List<Groups> getAllGroups() {
List<Groups> groups=new ArrayList<Groups>();
groups.add(new Groups(1, "Groups1"));
groups.add(new Groups(2, "Groups2"));
groups.add(new Groups(3, "Groups3"));
return groups;
}

}

Now the usuall way we call these services is via RestTemplate:


class RestTemplateRunner implements CommandLineRunner {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}

@Autowired
RestTemplate restTemplate;

@Override
public void run(String... args) throws Exception {

String result = restTemplate.getForObject("http://user-service/user/1", String.class);
System.out.println("=======RestTemplate=================");
System.out.println("RestTemplate RESULT===>" + result);

}

So we create a RestTemplate , autowire it and then call getForObject or any other method. We also have to provide the return type Class in order to map with our result type. This works great and since now RestTemplate is ribbon aware too, it will also chose the best server to hit.

As mentioned previously, Feign is a declarative style. So you work with Feign by providing one or more interfaces. You annotate interfaces with Feign annotation and for methods annoate by usual Spring MVC annotation. Spring cloud will implement this at run-time.

Lets see this by code. We will create a new project and create a interface UserServiceClient.java


import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.mynotes.spring.cloud.service.Groups;

@FeignClient("user-service")
public interface UserServiceClient {

@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
public String userDetails(@PathVariable("userId") int userId);

@RequestMapping(value = "/getAllGroups", method = RequestMethod.GET)
public List<Groups> getAllGroups();

}

Since this application is eureka client we just have to give the service name like @FeignClient(“user-service”).  Its not nessary to have a Eureka or spring cloud setup to use Feign. You can directly write the endpoint URL and it will work. Your actual server may also be written in a different langugage like php, .net etc. All you need to take care is the endpoint mapping @RequestMapping, to tell which way it needs to call the service. The method name can be anything but the return type and arguments must match to what is expected by the server. Thats it.

In application.yml we need to tell it about Eureka server location.


server:
  port: 8080

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

Lets see how will you call this. FeignClientApplication.java


import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.mynotes.spring.cloud.service.Groups;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class FeignClientApplication {

public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class, args);
}

}

@Component
class FeignRunner1 implements CommandLineRunner {
@Autowired
UserServiceClient userServiceClient;

@Override
public void run(String... args) throws Exception {
System.out.println("=================START FeignRunner1 Runner=============================");
String result=userServiceClient.userDetails(5);
System.out.println("=========FEIGN RESULT userDetails===>" + result);
List<Groups> groups=userServiceClient.getAllGroups();
System.out.println("=========FEIGN RESULT getAllGroups===>" + groups.size());
System.out.println("=================EXIT FeignRunner1 Runner==============================");
}

}

&nbsp;

So you just need @EnableFeignClients at your appilcation and @Autowired your UserServiceClient. Thats it. Now just call the method. and it will work.

spring-feign1

Whats happenig behind the seens is that during startups Spring scans for Feign annotations and then Feign provides the automatic runntime implementation for it. Ribbon is automatically enabled so it applies the load balancing part, then Feign handles the code.

Note that the Feign libraries are required at the runtime, so you will not get any compile time errors for Feign annoataions if the libraries are not present. Make sure your aplication gets them during runtime. For now we will keep them in our pom.


<?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>1.4.2.RELEASE</version>
<relativePath />
</parent>

<groupId>com.mynotes.spring.cloud</groupId>
<artifactId>feign-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>ribbon-client</name>
<description>Demo project for Spring Cloud cloud feign-client</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>com.mynotes.spring.cloud</groupId>
<artifactId>user-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

Notice that for we have user-service dependencies also, inorder to get the Group class. But its not necessary. As mentioned earlier, the server side apllication can be in a different language. In that case we just need to know the url and the type of json it returns. We can then create a class and map the json to it.

Advertisements
%d bloggers like this: