Spring Cloud Eureka Service Discovery Client Server Example
In this tutorial we demonstrate how to create and configure a service discovery client server environment using Netflix Eureka. First, we use spring-cloud
to create and configure a Service Discovery Registry Server. This server will listen to new nodes spinning up in our environment. Secondly, we create some Service Discovery Clients that will launch on multiple ports. The service discovery registry will automatically register these clients.
Netflix Eureka
Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers.
Service discovery is one of the key tenets of a microservice based architecture. Trying to hand configure each client or some form of convention can be very difficult to do and can be very brittle. Eureka is the Netflix Service Discovery Server and client. The server can be configured and deployed to be highly available, with each server replicating state about the registered services to the others.
Service Discovery: Eureka Server Example
Let’s start by creating the Eureka service discovery registry server.
Project Structure
Our project structure looks like the following.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.
<?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>
<groupId>com.memorynotfound.spring.cloud.eureka.service.discovery</groupId>
<artifactId>service-registry</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring CLOUD - ${project.artifactId}</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<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>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</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>
Configure Eureka Server
We configure the Eureka service discovery registry in a standalone mode. The combination of the two caches (client and server) and the heartbeat makes a standalone Eureka server fairly resilient to failure, as long as there is some sort of monitor or elastic runtime keeping it alive. In standalone mode, you might prefer to switch off the client side behaviour, so it doesn’t keep trying and failing to reach its peers. Here is an example configuration:
server:
port: ${PORT:8761} # Indicate the default PORT where this service will be started
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false # telling the server not to register himself in the service
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
logging:
level:
- ".=info"
- "com.memorynotfound=debug"
- "org.springframework=info"
Stand up a Eureka Service Registry
You can use Spring Cloud’s @EnableEurekaServer
annotation to standup a registry that other applications can talk to. This is a regular Spring Boot application with one annotation added to enable the registry. In production environment you’ll typically have multiple service registries for high availability.
package com.memorynotfound.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
Running The Service Registry
You can start the service registry using the following maven command.
mvn spring-boot:run
When the server is started, you can go to the admin panel by using the following url http://localhost:8761/
. You’ll receive the following output. Notice that we haven’t started any clients yet, so we have no instances available yet.
Spring Cloud Service Discovery: Eureka Client Example
Now we created the Service Registry, it’s time to create some clients. In the following section we’ll create a simple Eureka Client and deploy it multiple times on different ports.
When a client registers with Eureka, it provides meta-data about itself such as host and port, health indicator URL, home page etc. Eureka receives heartbeat messages from each instance belonging to a service. If the heartbeat fails over a configurable timetable, the instance is normally removed from the registry.
Project Structure
Let’s start by looking at the project structure.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.
<?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>
<groupId>com.memorynotfound.spring.cloud.eureka.service.discovery</groupId>
<artifactId>bookstore-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring CLOUD - ${project.artifactId}</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</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>
Configuring the Eureka Client
The default application name (service ID), virtual host and non-secure port, taken from the Environment
are spring.application.name
, spring.application.name
and server.port
respectively.
The Eureka instance is configured by eureka.instance.*
configuration keys, but the defaults will be fine if you ensure that your application has a spring.application.name
(this is the default for the Eureka service ID or VIP).
The eureka.instance.serviceUrl.defaultZone
is a magic string fallback value that provides the service URL for any client that doesn’t express a preference.
server:
port: ${PORT:8098}
# configure eureka client
eureka:
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 3
client:
serviceUrl:
defaultZone: http://user:password@localhost:8761/eureka/
# custom attribute for the actuator info endpoint
info:
app:
name: bookstore-service
# give the application a name
spring:
application:
name: bookstore-service
# disable actuagor security for endpoints
management:
security:
enabled: false
# custom logging settings
logging:
level:
- ".=info"
- "com.memorynotfound=debug"
- "org.springframework=info"
Writing a Client Rest Service
To demonstrate the workings of the client we are writing a simple rest service.
package com.memorynotfound.cloud;
import java.math.BigDecimal;
public class Book {
private Integer id;
private String name;
private BigDecimal price;
public Book() {
}
public Book(Integer id, String name, BigDecimal price) {
this.id = id;
this.name = name;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
The first service BookController
simply returns a List
of books.
package com.memorynotfound.cloud;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.*;
@RestController
public class BookController {
@GetMapping("books")
public List<Book> getBooks(){
return Arrays.asList(
new Book(1, "Spring Cloud Eureka Service Registry Server", new BigDecimal(0)),
new Book(2, "Spring Cloud Eureka Service Discovery", new BigDecimal(0)),
new Book(3, "Spring Cloud Eureka Client", new BigDecimal(0))
);
}
}
Using the EurekaClient
Once you have an app that @EnableDiscoveryClient
or (@EnableEurekaClient
) you can use it to discover service instances form the Eureka Server
. One way to do this is to use the native com.netflix.discovery.EurekaClient
(as opposed to the Spring Cloud DiscoveryClient
). In the following controller we use the EurekaClient
to retrieve all the instances from the ‘cluster’.
package com.memorynotfound.cloud;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Applications;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DiscoveryController {
@Autowired
private EurekaClient eurekaClient;
@GetMapping("/applications")
public Applications getApplications() {
return eurekaClient.getApplications();
}
}
Stand up Service Discovery Eureka Client
package com.memorynotfound.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class BookStoreApplication {
public static void main(String[] args) {
SpringApplication.run(BookStoreApplication.class, args);
}
}
Running The Eureka Client
We start multiple clients by using the following commands. Make sure you use different ports.
# start the first client
mvn spring-boot:run -Drun.arguments="--PORT=8098"
# start the second client
mvn spring-boot:run -Drun.arguments="--PORT=8099"
Example output
When the clients are booted and you access the admin page again: http://localhost:8761/
, you’ll see the service registry discovered 2 instances.
When we access the following URL: http://10.0.63.74:8091/applications
. we receive detailed meta-data information about our instances.
When we access the following URL: http://10.0.63.74:8091/books
. we receive the following output.
References
- Spring Cloud Eureka Clients Documentation
- Spring Cloud Eureka Server Documentation
- EurekaClient Source Code Doc