Spring Cloud Open Feign

Make REST Communication easy

Posted by ChenRiang on July 11, 2021

In a microservice world, every service is recommended to be small and independent. As a developer, it is fortunate because we only need to look work on a relatively small code base. However, the drawback of microservice is that the developer has to implement the intercommunication between micro service which introduce extra complexity. For a developer that coming from monolith world, you might be asking why should I need to care about the details of REST API, shouldn’t we should spend the time to implement business logic?

Spring address this issue with Feign, a declarative web client.

Overview

To illustrate how can we use Feign, we will try to implement 2 simple microservice service.

We will create an abstartction layer to define the contract of the REST communication. In real world, this two service might maintained by different team, by defnining the contract of REST Service it could de-couple the process dependency.

  • discovery-api (REST API)

    • REST API contract
  • discovery-service (REST SERVICE)

    • GET Method - return some value

    • Response example:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      [
          {
              "id": "id1",
              "name": "Jason",
              "score": "500"
          },
          {
              "id": "id1",
              "name": "Max",
              "score": "500"
          },
          {
              "id": "id1",
              "name": "Matt",
              "score": "500"
          }
      ]
      
  • dashboard-service (REST CLIENT)

    • GET Method - do a REST call to discovery-service and return highest score and ranking

    • Response example:

      1
      2
      3
      4
      
      {
          "topScore": "5000",
          "ranking": "Matt,Max,Jason"
      }
      

Implementation

discovery-api (REST API)

We will create an simple interface which will be implement / extend by discovery-serice and dashboard-service later on.

1
2
3
4
5
6
7
@RequestMapping("/discovery")
public interface DiscoveryService {
    
    @GetMapping
    List<DiscoveredItem> discover();

}

A POJO class DiscoveredItem is created to represend the response.

1
2
3
4
5
6
7
8
@AllArgsConstructor //lombok
@EqualsAndHashCode  //lombok
@Data  				//lombok
public class DiscoveredItem {
    private String id;
    private String name;
    private String score;
}

discovery-service (REST SERVICE)

In this section, we will implement the contract interface that created in discovery-api.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class DiscoveryController implements DiscoveryService {
    @Override
    public List<DiscoveredItem> discover() {
        List<DiscoveredItem> discoveredItems = new ArrayList<>();

        // return some random value
        discoveredItems.add(new DiscoveredItem("id1", "Jason", "5000"));
        discoveredItems.add(new DiscoveredItem("id2", "Max", "500"));
        discoveredItems.add(new DiscoveredItem("id3", "Matt", "1000"));

        return discoveredItems;
    }
}

dashboard-service (REST CLIENT)

  1. Declare the feign dependency in maven pom.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    <properties>
        <java.version>1.8</java.version>
        <spring.cloud.version>2020.0.2</spring.cloud.version>
    </properties>
       
    <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>
       
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
    
  2. Enable feign in Springboot application

    1
    2
    3
    4
    5
    6
    7
    8
    
    @SpringBootApplication
    @EnableFeignClients
    public class DashboardApplication {
        public static void main(String[] args) {
            SpringApplication.run(DashboardApplication.class, args);
        }
    }
       
    
  3. Extend the DiscoveryService created in discovery-api and mark it as feign client.

    1
    2
    3
    4
    
    @FeignClient(value = "discoveryServiceClient", url = "${discovery.service.host}")
    public interface DiscoveryServiceClient extends DiscoveryService {
    }
       
    
    • ${discovery.service.host} - application.yaml configuration
  4. Autowired feign client to make a REST service call.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    @RestController
    @RequestMapping("/dashboard")
    public class DashboardController {
       
        @Autowired
        private DiscoveryServiceClient discoveryServiceClient;
       
        @GetMapping
        public Map<String, String> getDashboardDetails() {
            Map<String, String> details = new HashMap<>();
       
            List<DiscoveredItem> discoveredItems =
                discoveryServiceClient.discover();
       
            String topScore = discoveredItems.stream()
                    .max(Comparator.comparing(DiscoveredItem::getScore))
                    .get()
                    .getScore();
       
            String ranking = discoveredItems.stream()
                    .sorted(Comparator.comparing(DiscoveredItem::getScore))
                    .map(DiscoveredItem::getName)
                    .collect(Collectors.joining(","));
       
            details.put("topScore", topScore);
            details.put("ranking", ranking);
            return details;
        }
    }
    

Conclusion

In this article, we implemented the REST communication via a declarative way. With Feign, developer can write lesser code and improve the code readability at the same time.

Check out the source code here.