Client Side Load Balancer Ribbon with Feign and Spring cloud for Microservices

Using Client Side Load Balancer Ribbon with Feign and Spring cloud for Microservices

Netflix Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients unlike traditional server side load balancing the configuration is stored on the application as an inner component so that the service itself can choose which instance to send the request to. Feign already uses Ribbon.

Why using Client Side Load Balancer Ribbon?

The response is simple:

  • Service discovery: Ribbon is easy to integrate with Eureka Naming Server, ZooKeeper, etc.

  • Load balancing
  • Fault tolerance
  • Configurable rules
  • Caching

In this tutorial we are going to create a Spring Cloud Ribbon web service using a Spring Boot microservices application. The example illustrate how ribbon can take care of load balancing between different instances of a web microservice. We will create an app (port 8000) that uses ribbon to communicate to multiple instances of our microservice (on port 8101, 8100) that is simply running in multiple port and returning the port that is running on.

Client Side Load Balancer Ribbon with Feign and Spring cloud for Microservices Schema

1 Microservice To be called by Feign and Ribbon

The following microservice has only one end-point which is "/calculator/coefficient/{value}" that get the value and return it with the port. In order to externalize configuration read this article: Spring Cloud Config Server and Client.

spring.application.name=microservice2
server.port=8101

package com.yamicode.microservice2.rest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("calculator")
public class CalculatorRest {

    @Value("${server.port}")
    private int port;

    @GetMapping("coefficient/{value}")
    public Map sum(@PathVariable int value){
        Map result = new HashMap();
        result.put("value", value);
        result.put("port", port);
        return result;
    }
}


To run the app on multiple port just copy the class Main and change the configuration on the VM option and add the "-Dserver.port=newPort" option (i'm using 8100 and 8101).

Changing configuration on the VM option to run instance of different port

2 Spring Cloud Netflix Ribbon

package com.yamicode.microservice1.rest.feign;

import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Map;

@Component
@FeignClient(name = "microservice2")
@RibbonClient(name = "microservice2")
@RequestMapping("calculator")
public interface FeignClientApiGateWay {

    //GET example
    @RequestMapping(method = RequestMethod.GET, value = "/coefficient/{value}", consumes = "application/json")
    Map calculate(@PathVariable("value") int value);

}

package com.yamicode.microservice1.rest;

import com.yamicode.microservice1.rest.feign.FeignClientApiGateWay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("calculator")
public class CalculatorRest {

    @Autowired
    private FeignClientApiGateWay feignClientApiGateWay;

    @GetMapping("coefficient/{value}")
    public Map sum(@PathVariable int value){
        return feignClientApiGateWay.calculate(value);
    }
}

spring.application.name=microservice1
server.port=8000
microservice2.ribbon.listOfServers=localhost:8101,localhost:8100
package com.yamicode.microservice1.rest;

import com.yamicode.microservice1.rest.feign.FeignClientApiGateWay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("calculator")
public class CalculatorRest {

    @Autowired
    private FeignClientApiGateWay feignClientApiGateWay;

    @GetMapping("coefficient/{value}")
    public Map sum(@PathVariable int value){
        return feignClientApiGateWay.calculate(value);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<project>
	<!--Rest-->
	<properties>
		<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.1.3.RELEASE</version>
        </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>

	<!--Rest-->

</project>

3 Client Side Load Balancer Ribbon Result

Client Side Load Balancer Ribbon with Feign and Spring cloud for Microservices Testing