In this article, we will look at Spring Cloud Netflix Eureka Server and Spring Cloud Netflix Eureka Client. We will develop Eureka Server and Eureka Clients. The eureka clients will call other clients using Eureka server service discovery.
Introduction
In this tutorial, we will develop Spring Cloud Netflix Eureka Server and Spring Cloud Netflix Client, for simplicity, we will refer to them as Eureka server and Eureka client in this article. To show how it works, we will build one Eureka Server and a couple of Eureka clients and show the communication between these Eureka client applications using Eureka Server. We will use the postman application to show the actual demo.
1. What is Eureka Server and Client?
Eureka Server is an application also known as service discovery. It holds the details on all the Eureka client applications which are registered to it. Eureka server will know all the details like IP addresses and ports for all the Eureka client applications (microservices) registered to it. It will help the communication between Eureka client applications.
Eureka Client is nothing but a microservice and we make it Eureka client using @EnableEurekaClient or @EnableDiscoveryClient and with Eureka server URL in properties, it will register itself to the Eureka server. Eureka client comes in the Spring Cloud.
In the application.properties
file, we can also set the time when the client will send the heartbeat to the server and also the time when the client expires if it fails to send the heartbeat in that time period.
2. Eureka Server Application Setup
Let’s start by setting up the Eureka server. We will use the visual interface Spring Initializer to use generate the initial application structure. If you like, you can use IDE to build the module (most IDE integrate with Initializr).
We are using Java 8; Spring Boot, and we are adding just one dependency that is Eureka Server (Spring Cloud Discovery). Client “GENERATE” and Spring Initializer will create an initial structure of the project with the dependencies. Once generated, please unzip the folder and create a new project in your favorite IDE. Here we are using IntelliJ IDE. We will see the following project structure:
Here is how our module look like in IntelliJ:
3. Eureka Server Application Overview
Let us go over the application and its important aspects. This is how our pom.xml look like:
<?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 https://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.2.6.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eurekaserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaserver</name>
<description>Eureka Server Application</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
3.1. application.properties:
spring.application.name=eurekaserver
server.port=9090
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
Let’s look at above properties
- server port–Port where the Eureka server will run.
- register-with-eureka–it will be false as it is the server itself and it doesn’t have to register itself.
- fetch-registry–this is also client-specific and hence it will be false. The client uses this to fetch the registry information from the server and caches locally.
- serviceUrl–this is a URL for the eureka server and the clients would use this URL to register with the server.
3.2. Eureka Server Main Class
Let’s define the main class which will bootstrap the server.
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
The @EnableEurekaServer
annotation helps the spring boot application to run as Eureka Server.
4. Eureka Client 1 Setup
We finished the Eureka Server basic setup, let’s create the Eureka client application. We will use the visual interface Spring Initializer to use generate the initial application structure.
We are using Java 8; Spring Boot 2.2.6, and we are adding just one dependency that is Eureka Server (Spring Cloud Discovery). Client “GENERATE” and Spring Initializer will create an initial structure of the project with the dependencies. Once generated, please unzip the folder and create a new project in your favorite IDE, here we are using IntelliJ IDE. We will see the following project structure:
Project Structure:
5. Eureka Client1 Application Overview
Here is a complete look at the first Eureka Client pom.xml file. We have added Netflix Eureka client as dependency for our application. Here is the complete view of pom.xml
file.
<?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 https://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.2.6.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eurekaclient1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaclient1</name>
<description>Eureka Client 1</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<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-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
5.1. application.properties file:
spring.application.name=eurekaclient1
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:9090/eureka/
- Server port–a unique port for the client application.
- lease-renewal-interval-in-seconds–It is the interval post which the client will send the heartbeat to the server.
- lease-expiration-duration-in-seconds–number of seconds eureka server will wait for the heartbeat from the client, otherwise it will remove the client from the registry.
- service-url–the URL for the server.
5.2. Eureka Client Application Main class
package com.example.eurekaclient1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableDiscoveryClient
@SpringBootApplication
public class Eurekaclient1Application {
public static void main(String[] args) {
SpringApplication.run(Eurekaclient1Application.class, args);
}
}
The @EnableDiscoveryClient
annotation activates the Netflix discovery clients.
5.3. Controller Class
Let’s define a controller class for our Eureka client. This is a simple Spring Boot REST controller and define few endpoints to provide product information to the calling client.
package com.javadevjournal.eurekaclient1.controller;
import com.javadevjournal.eurekaclient1.model.Product;
import com.sun.jersey.core.header.MediaTypes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.UUID;
@RestController
public class Eurekaclient1Controller {
@Autowired
private LoadBalancerClient loadBalancerClient;
private RestTemplate restTemplate = new RestTemplate();
@GetMapping("/calleurekaclient1")
public ResponseEntity callEurekaClient1(){
return new ResponseEntity("Hello From Client 1 ", HttpStatus.OK);
}
@GetMapping("/callEurekaClient2viaClient1")
public ResponseEntity callClient2(){
try {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/calleurekaclient2", String.class), HttpStatus.OK);
}catch (Exception exp) {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/calleurekaclient2", String.class), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping(value = "/getallproducts")
public ResponseEntity getAllProductsFromClient2(){
try {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/allproducts", String.class), HttpStatus.OK);
}catch (Exception exp) {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/allproducts", String.class), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping(value = "/getproductbyid/{id}")
public ResponseEntity getProducByIDFromClient2(@PathVariable UUID id){
try {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/product/"+id, String.class), HttpStatus.OK);
}catch (Exception exp) {
return new ResponseEntity(
restTemplate.getForObject(getEurkaClient2BaseUri() + "/product/"+id, String.class), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/deleteproductbyid/{id}")
public void deleteProductByIDFromClient2(@PathVariable UUID id){
try {
restTemplate.delete(getEurkaClient2BaseUri() + "/product/delete/"+id);
}catch (Exception exp) {
//log the error
}
}
@PostMapping(value = "/updteproduct/")
public ResponseEntity updateProductFromClient2(){
try {
return new ResponseEntity(
restTemplate.postForObject(getEurkaClient2BaseUri() + "/product/update/",new Product(UUID.randomUUID(), "Soap" , 99.99, "Body"),Product.class ), HttpStatus.OK);
}catch (Exception exp) {
return new ResponseEntity(
restTemplate.postForObject(getEurkaClient2BaseUri() + "/product/update/",new Product(UUID.randomUUID(), "Soap" , 99.99, "Body"),Product.class ), HttpStatus.OK);
}
}
private String getEurkaClient2BaseUri(){
ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKACLIENT2");
return serviceInstance.getUri().toString();
}
}
Let’s take a look at some additional important points.
- LocalBalanceClient – it is a client-side load balancer.
- ServiceInstance – this represents an instance of a service in a discovery service like Eureka Server.
- ReposnseEntity – it is the combination of status code, headers, and actual response body.
@RestController
– it is a combination of@Controller
and@ResponseBody
– it helps map the resources and also sends the response back in XML and JSON format.
6. Eureka Client 2 Setup
Let’s define our second Eureka client.We will use the visual interface Spring Initializer to use generate the initial application structure.
We are using Java 8; Spring Boot 2.2.6, and we are adding just one dependency that is Eureka Server (Spring Cloud Discovery).Client “GENERATE” and Spring Initializer will create an initial structure of the project with the dependencies. Once generated, please unzip the folder and create a new project in your favorite IDE, here we are using IntelliJ IDE. We will see the following project structure:
Project Structure:
7. Eureka Client2 Application Overview
pom.xml
<?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 https://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.2.6.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eurekaclient2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaclient2</name>
<description>Eureka Client 2</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<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-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
7.1. The application.properties
spring.application.name=eurekaclient2
server.port=8085
eureka.client.service-url.defaultZone=http://localhost:9090/eureka/
7.2. Main Class
package com.example.eurekaclient2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class Eurekaclient2Application {
public static void main(String[] args) {
SpringApplication.run(Eurekaclient2Application.class, args);
}
}
7.3. Client 2 (Product service) Controller:
package com.javadevjournal.eurekaclient2.controller;
import com.javadevjournal.eurekaclient2.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
public class Eurekaclient2Controller {
private static List productList = new ArrayList<>();
static{
productList = new ArrayList<>();
Product p1 = new Product(UUID.randomUUID(), "Guitar" , 99.99, "Music");
Product p2 = new Product(UUID.randomUUID(), "AWS Book" , 29.99, "Books");
Product p3 = new Product(UUID.randomUUID(), "Bread" , 9.99, "Food");
Product p4 = new Product(UUID.randomUUID(), "Soap" , 99.99, "Body");
Product p5 = new Product(UUID.randomUUID(), "Milk" , 3.99, "Food");
Product p6 = new Product(UUID.randomUUID(), "Pant" , 17.99, " Cloth");
Product p7 = new Product(UUID.randomUUID(), "Tyre" , 39.99, "Auto");
productList.add(p1);
productList.add(p2);
productList.add(p3);
productList.add(p4);
productList.add(p5);
productList.add(p6);
productList.add(p7);
}
@GetMapping("/calleurekaclient2")
public ResponseEntity callEurekaClient2(){
return new ResponseEntity("Hello From Client 2", HttpStatus.OK);
}
@GetMapping(value = "/allproducts", produces = MediaType.APPLICATION_JSON_VALUE)
public List list() {
return Eurekaclient2Controller.productList;
}
@GetMapping(value = "/product/{id}" , produces = MediaType.APPLICATION_JSON_VALUE)
public Product showProduct(@PathVariable UUID id) {
return new Product(id, "Guitar" , 99.99, "Music");
}
@PostMapping(value = "/product/update")
public String saveProduct(@RequestBody Product product) {
return "Product with product id: "+ product.getProductId() +" and product name:"+product.getProductName()+" has been saved successfully!";
}
@DeleteMapping(value = "/product/delete/{id}")
public void delete(@PathVariable UUID id) {
//log "Product "+ id +" has been deleted successfully!";
}
}
8. Service Discovery
We will start the Eureka Server application first, once it is up and running, we will access the Eureka Server using http://localhost:9090/. We are running it from our local machine.We can see we don’t have any clients listed (check Applications).
Once the server is up:
Now we will start all the clients one by one and once they are all up and running, we will go to Eureka Server again and our clients will be listed under Application and with their IP addresses and ports.
Once all the clients up:
Now, we will test our server and clients using postman application:
Calling Client1 – It is a simple HTTP get call
Calling Client2 – It is a simple HTTP get call
Client2 (Product service) via Client1 – Here we will use LoadBalancerClient and ServiceInstance libraries to get the client2 application details within client1 and call the method of client2.
GetProductById: Calling Client2 (Product service) via Client1, it is like the above call but this time the client2 will return the product by the id. This is an HTTP get call.
GetAllProducts: Calling Client2 (Product service) via Client1, it is like the above call but this time the client2 will return the list all products. This is an HTTP get call.
DeleteProductById: Calling Client2 (product service) via Client1, it is like the above call, but this time the client2 will delete the product from the product list by the id. This is an HTTP DELETE call.
9. What is Feign Client?
Feign client is a declarative web service client in the spring cloud bundle. It provides an annotation to abstract rest-calls and helps rest clients communicate with each other without writing details rest-client codes. We will need to use @EnableFeignClients annotation to mark our spring boot application as Feign client.
Please have a look below:
package com.example.feignclient.contrller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name= "greetings")
public interface IFeignClient {
@GetMapping(value= "/greet/welcome/{message}")
public String getGreetings(@PathVariable(name= "message") String langCode);
}
For more detail, read Introduction to Feign
Summary
In this post, we discussed the Spring Cloud Netflix Eureka Server and Spring Cloud Netflix Eureka Client. Here is a quick summary of the items discussed in this post.
- What are Eureka Server and Eureka Client?
- We have set up applications for Eureka Server Clients.
- Understood various libraries in under Spring Cloud.
- Seen the working on service discovery using Postman
The source code for this application is available on GitHub.