Spring Cloud - Eureka

Let’s learn Service Discovery and use Eureka as a service registry.

Service Discovery

Nowadays services are usually running on the cloud and have dynamic network locations. Network locations changes when service is restarted or upgraded. Locating such service with a configuration file just doesn’t work. Service Discovery can solve this problem

There are two main service discovery patterns:

  • client‑side discovery - application service register its location to central registry. The client queries service registry to get the available service location and then make a request to one of the available service. The Biggest drawback is clients has to know how to talk to the service registry and this creates tight coupling between service registry and clients. Example Client-side discovery registry are Zookeeper, Etcd
  • server‑side discovery - The client makes a request to a service via load balancer. The load balancer queries the service registry and routes each request to an available service instance. AWS Elastic Load Balancer(ELB) is an example of server-side discovery router.

Eureka

Eureka is a REST 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.

Eureka is an example of client-side discovery service registry.

Remember that Eureka 1.x is still active. The work on Eureka 2.0 is discontinued.

Eureka Comes with Two Components

  • Eureka Server
  • Eureka Client

There are two types of applications that uses Eureka

  • Application Client - use Eureka Client to make requests to the Application Service.
  • Application Service - receives requests from Application Client and sends a response back.

Eureka Server

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</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>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

main application - @EnableEurekaServer annotation shows it is a Eureka Server

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {

public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}

}

application.properties file

1
2
3
4
server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Properties explained

  • eureka.client.register-with-eureka Register itself as a Client, default is true
  • eureka.client.fetch-registry fetch registry from eureka server, default is true
  • eureka.client.serviceUrl.defaultZone comma separated list of peers

Now Eureka Server is running at http://localhost:8761/

Eureka Clients

All clients need to have eureka-client dependency in pom.xml

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

ClientAController.java - A very simple Controller

1
2
3
4
5
6
7
@RestController
public class ClientAController {
@RequestMapping("/msg")
public String sayHi() {
return "message from server1.";
}
}

ClientA application.properties file

1
2
3
4
5
6
spring.application.name=eureka-client-a

server.port=8000

# eureka server url
eureka.client.service-url.default-zone=http://localhost:8761/eureka

@EnableEurekaClient can be added to the main class, but this is optional.

ClientBController.java - note that it can refer to clientA host using the registered application name.

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class ClientBController {
@Autowired
private RestTemplate restTemplate;

@RequestMapping("/getMsgFromClientA")
public String getMsgFromClientA() {
String msgFromClientA = restTemplate.getForObject("http://eureka-client-a/msg", String.class);
return "Message From ClientA: " + msgFromClientA;
}
}

ClientB application.properties file

1
2
3
4
5
6
spring.application.name=eureka-client-b

server.port=8001

# eureka server url
eureka.client.service-url.default-zone=http://localhost:8761/eureka

Configuration for RestTemplate dependency. It has @LoadBalancered annotation so that it can resolve application host name and do load balance.

1
2
3
4
5
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}

output for URL http://localhost:8001/getMsgFromClientA

1
Message From ClientA: message from server1.

Feign Rest Client

OpenFeign is a declarative REST client for Spring Boot apps. Feign makes writing java http clients easier. You can use Feign to replace RestTemplate.

Maven Dependency

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

EurekaClientBApplication.java - add @EnableFeignClients annotation to top level Spring boot Application class.

1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class EurekaClientBApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientBApplication.class, args);
}
}

Create FeignClient Interface. Feign Client has @FeignClient annotation.

1
2
3
4
5
@FeignClient(value="eureka-client-a")
public interface MyFeignClient {
@RequestMapping("/msg")
String getMsgFromClientA();
}

ClientBController class - replace RestTemplate with Feign client.

1
2
3
4
5
6
7
8
9
10
@RestController
public class ClientBController {
@Autowired
private MyFeignClient myFeignClient;

@RequestMapping("/getMsgFromClientA")
public String getMsgFromClientA() {
return "Message From ClientA: " + myFeignClient.getMsgFromClientA();
}
}

Feign supports Hystrix. To enable Hystrix, add feign.hystrix.enabled=true to application.properties file. You can also implement fallback method when the service call fails. For more information on OpenFeign, see Spring Cloud OpenFeign by Baeldung

Eureka Server Cluster

In production, we usually need to have 3 or more instances of registration service for High Availability. Each eureka service knows each other.

application.yml file with 3 different profiles.

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
30
31
32
33
34
35
36
37
38
39
---
spring:
application:
name: eureka-server
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2:8762/eureka/, http://peer3:8763/eureka/
---
spring:
application:
name: eureka-server
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/, http://peer3:8763/eureka/
---
spring:
application:
name: eureka-server
profiles: peer3
server:
port: 8763
eureka:
instance:
hostname: peer3
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/, http://peer2:8762/eureka/

Add the following to /etc/hosts file when you are running the cluster in local machine.

1
2
3
127.0.0.1 peer1
127.0.0.1 peer2
127.0.0.1 peer3

Spring Cloud Eureka Docs

Reference