Service discovery with Netflix Eureka and Ribbon Client Load Balancer

Spring Cloud Netflix provides an easy Cloud integration for Spring Boot apps through autoconfiguration and binding to the Spring Environment.

By adding few simple annotations you can quickly leverage and configure the common patterns inside your application and build large distributed systems with Netflix components. In this tutorial we will cover Service Discovery (Eureka) and Client Side Load Balancing (Ribbon).

Service Discovery with Netflix Eureka

By using Netflix Eureka, you can register multiple services in a Cloud environment. These register instances of the service act as a server and replicate its status to the connected peer clients. Netflix Eureka manages the requests to the instances of a service using a load balancing algorithm. The client retrieves a list of all connected instances of a service registry and distributes the loads to these instances using a load-balancing algorithm, such as Client Side Load Balancing (Ribbon) does.

Let’s start by implementing the server-side service registry (Eureka Server) first. Open the Terminal (or use Spring Initializr) and create a project with the following dependencies:

spring init -n eureka-server -dcloud-eureka-server 

The following dependencies will be added:

  <properties>     <java.version>1.8</java.version>     <spring-cloud.version>Greenwich.SR2</spring-cloud.version>   </properties>    <dependencies>     <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>     </dependency>      <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-test</artifactId>       <scope>test</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> 

Now enable the Eureka server by using the @EnableEurekaServer annotation in a main Spring Boot application class that is also annotated with the @SpringBootApplication annotation.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class, args);
  }
}

That’s all you need to configure an Eureka Server. In terms of configuration, define in the application.yml the following properties:

eureka:   client:     registerWithEureka: false     fetchRegistry: false server:   port: 8761 
  • registerWithEureka: If we make this property as true then while the server starts the inbuilt client will try to register itself with the Eureka server.
  • fetchRegistry: The inbuilt client will try to fetch the Eureka registry if we configure this property as true.
Replica node URL:  http://localhost:8761/eureka/  Changing status to UP Started Eureka Server Tomcat started on port(s): 8761 (http) with context path '' Updating port to 8761 

we can point our browser to http://localhost:8761 to view the Eureka dashboard as follows:

spring cloud eureka ribbon tutorial spring cloud eureka ribbon tutorial

Implementing Service Discovery—Eureka Clients

Now that we have bootstrapped Eureka, we will add an Eureka Client. To do that, create a project with the following dependencies:

spring init -n eureka-client -dcloud-eureka,web 

The following dependencies will be added:

  <properties>     <java.version>1.8</java.version>     <spring-cloud.version>Greenwich.SR2</spring-cloud.version>   </properties>    <dependencies>     <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>     </dependency>      <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-web</artifactId>     </dependency>     <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-test</artifactId>       <scope>test</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> 

Registering a client with Eureka means that a client provides its own meta-information (hostname, port, health indicator URL, and homepage). Each instance of a service sends heartbeat messages to the Eureka server; if Eureka doesn’t receive the heartbeat over a configurable timetable, the instance is removed from the registry.

Now lets enable the Application class to be an Eureka Client, by adding the annotation @EnableEurekaClient:

package com.example.eurekaclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaClientApplication.class, args);
  }
}

We will then create a REST service to be registered with the Client:

package com.example.eurekaclient;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
  @GetMapping("/hello")
  public String sayHello() {
    return "Hello World from EurekaClient!";
  }
}

Now let’s add the application.yml configuration file for this client application as follows:

spring:   application:     name: spring-cloud-eureka-client  server:   port: 8180  eureka:   client:     service-url:       default-zone: ${EUREKA_URI:http://localhost:8761/eureka}   instance:     prefer-ip-address: true 

Start the application. You should see on your Eureka Server Console that the service was registered:

2019-09-02 19:54:43.424  INFO 32676 --- [nio-8761-exec-2] c.n.e.registry.AbstractInstanceRegistry  : Registered instance SPRING-CLOUD-EUREKA-CLIENT/fedora.station:spring-cloud-eureka-client:8180 with status UP (replication=false) 2019-09-02 19:54:44.063  INFO 32676 --- [nio-8761-exec-3] c.n.e.registry.AbstractInstanceRegistry  : Registered instance SPRING-CLOUD-EUREKA-CLIENT/fedora.station:spring-cloud-eureka-client:8180 with status UP (replication=true) 2019-09-02 19:54:50.596  INFO 32676 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry  : Running the evict task with compensationTime 0ms 

And conversely on the Client console:

[nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_SPRING-CLOUD-EUREKA-CLIENT/fedora.station:spring-cloud-eureka-client:8180 - registration status: 204 2019-09-02 19:55:13.258  INFO 576 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Disable delta property : false 

If you browse to http://localhost:8761 to view the Eureka dashboard you should see the Client that has been discovered:

spring cloud eureka ribbon tutorial spring cloud eureka ribbon tutorial

Client-side load balancing using Netflix Ribbon

Client-side load balancing is used to balance requests to the microservices. Implementing client-side load balancing provides a way to distribute the load across multiple instances. The Discovery server returns the location of these multiple instances. These instances are only for resilience and load-sharing, but the client needs to pick only one instance of the service. Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients.

We can bootstrap a Ribbon Cloud application as follows:

 spring init -n client-ribbon -dthymeleaf,web,cloud-ribbon,cloud-eureka 

The following dependencies will be added:

 <properties>     <java.version>1.8</java.version>     <spring-cloud.version>Greenwich.SR2</spring-cloud.version>   </properties>    <dependencies>     <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-thymeleaf</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-netflix-eureka-client</artifactId>     </dependency>     <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>     </dependency>      <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-test</artifactId>       <scope>test</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> 

Our application will include the @EnableEurekaClient but also the @LoadBalanced, which is placed on a method that returns an instance of a RestTemplate:

package com.example.ribbonclient;

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.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class RibbonClientApplication {
  public static void main(String[] args) {
    SpringApplication.run(RibbonClientApplication.class, args);
  }

  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

The RestTemplate is autowired in the following Service class, which binds the request to the Eureka Client we have just registered:

package com.example.ribbonclient.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class HelloServiceClient {
  @Autowired @LoadBalanced RestTemplate restTemplate;

  public String sayHello() {
    return restTemplate.getForObject("http://SPRING-CLOUD-EUREKA-CLIENT/hello", String.class);
  }
}

Finally, we include a simple Controller which makes the Ribbon client balancer available at the “/hello” mapping:

package com.example.ribbonclient.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.ribbonclient.service.HelloServiceClient;

@Controller
public class HelloController {
  @Autowired HelloServiceClient helloServiceClient;

  @GetMapping("/hello")
  String sayHello(ModelMap model) {
    model.put("message", helloServiceClient.sayHello());
    return "hello";
  }
}

We will also include a static hello.html which shows the message text as thymeleaf tag:

<html xmlns:th="http://www.thymeleaf.org">
         
    <body>
                 
        <h2 th:text="${message}"></h2>
             
    </body>
     
</html>

The last piece of information is the application.yml file for the Ribbon Client:

spring:   application:     name: spring-cloud-ribbon-client  server:   port: 8181  eureka:   client:     service-url:       default-zone: ${EUREKA_URI:http://localhost:8761/eureka}   instance:     prefer-ip-address: true 

Now run this client application and open the browser at http://localhost:8761/ :

spring cloud eureka ribbon tutorial spring cloud eureka ribbon tutorial

As you can see, on the Eureka Dashboard, there are two services registered as SPRING-CLOUD-EUREKA-CLIENT , and SPRING-CLOUD-RIBBON-CLIENT.

After running this example, we’ll open our browser and go to http://localhost:8181/hello and it should display something like the following:

spring cloud eureka ribbon tutorial spring cloud eureka ribbon tutorial

Congrats! You just managed to run your an Eureka Client-Server application with a LoadBalancer Ribbon Client. In the next tutorial, we will show how to use a Feign Client, to simplify the creation of Netflix Clients: Getting Started with NetFlix Feign Client

Source code for this article is available at: https://github.com/fmarchioni/masterspringboot/tree/master/cloud/eureka