Looking for an Expert Development Team? Take 2 weeks Free Trial! Try Now
×Start 2 Week Free Trial
Microservices Using Spring Cloud Netflix – Eureka With Demo
1. Introduction
Microservice architecture is a buzzword nowadays. As opposed to traditional monolithic applications, an application is deployed as a suite of smaller and independent applications each one managing a separate business operation. These mini-applications can be independently developed (even in different languages), scaled up/down, and deployed on a single server or across different servers.
To accomplish a functionality, it’s often required for these services to communicate with each other. They typically communicate using REST API. For service A to access APIs of service B, it has to know its host and port. Hardcoding is not the right way, since this means they are tightly coupled.
The solution here is to have an equivalent of a Telephone Directory for services, where each service registers itself and Our Java programmers in India can simply fetch the desired service by its name.
There are various popular service registries, like Netflix Eureka, CONSOL, and Zookeeper. In this article, we’ll focus on the Netflix Eureka.
2. Netflix Eureka
A Netflix Eureka is a popular service registry and discovery service. Netflix built it and later open-sourced it.
Eureka server is nothing but simply a Microservice, tasked with maintaining a registry of all the Microservice.
Spring Cloud Config supports seamless integration with Eureka with declarative configuration.
Each service registers itself with Eureka Server and tells it where it resides (host, port).
Service a can look up to Eureka Server and get details of Service B (provided both of them are registered) via simply using its SERVICEID.
3. Project
In this project, we will be creating 3 Microservice:
Eureka server - service registry
User service – a Eureka registered Microservice providing some endpoints
Info Service – a Eureka registered Microservice, providing its own end-points as well as using Eureka to consume end-points of User service too.
These services will be created using Spring-Boot as well as we’ll be using Spring-Cloud configuration.
3.1 Eureka Server:
To implement the Eureka server, all we need to do is:
Add spring-cloud-starter-eureka-server dependency
Enable Eureka Server by adding annotation @EnableEurekaServeron our main spring boot application class.
The project structure is as follows:
3.1.1 Maven Dependencies
4.0.0com.hemanteureka-server0.0.1-SNAPSHOTjareureka-serverDemo project for Spring Bootorg.springframework.bootspring-boot-starter-parent1.5.1.RELEASEUTF-8UTF-81.8org.springframework.bootspring-boot-starter-weborg.springframework.cloudspring-cloud-starter-eureka-serverorg.springframework.bootspring-boot-starter-testtestorg.springframework.cloudspring-cloud-dependenciesCamden.SR5pomimportorg.springframework.bootspring-boot-maven-plugin
3.1.2 Spring boot application class
package com.hemant.eurekaserver;
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);
}
}
3.1.3 application.yaml
In addition to athe bove, we have to specify the following configuration in application.yaml file (we can alternatively use application. Properties file).
# Server port
server:
port: 8761
eureka:
client:
# Dont register itself with eureka
registerWithEureka: false
fetchRegistry: false
Explanation:
Here we have designated port – 8761 to run Ethe eureka server.
The property eureka.client.registerWithEureka= false: Eureka Clients register with Eureka Server. The registration happens on the first heart-beat signal. Also, clients send periodic heartbeats to Eureka Server to update the registry. Since this is Eureka Server itself, we don’t need it.
The property eureka.client.fetchRegistry = false is used by clients to fetching the registry information from the Eureka server. After that, these clients use that information to find other services. Clients update this information periodically (by default every 30 sec). Since this is Eureka Server itself, we don’t need it.
3.2 User Service
This is a simple spring boot service that acts as a Eureka Client and gets registered with our Eureka Server.
The project structure is as follows:
3.2.1 Maven Dependencies
4.0.0com.hemantuser-service0.0.1-SNAPSHOTjaruser-serviceDemo project for Spring Bootorg.springframework.bootspring-boot-starter-parent1.5.1.RELEASEUTF-8UTF-81.8org.springframework.bootspring-boot-starter-weborg.springframework.cloudspring-cloud-starter-eureka1.2.6.RELEASEorg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-maven-plugin
3.2.2 Sping boot Application class
package com.hemant.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
The convenience annotation @EnableEurekaClient makes this application a Eureka Client which can be registered with our Eureka Server.
3.2.3 REST API
This application also exposes a few REST END points. The model is a simple User class:
package com.hemant.userservice.model;
public class User {
private String id;
private String name;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof User))
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", email=" + email + "]";
}
public User(String id, String name, String email) {
super();
this.id = id;
this.name = name;
this.email = email;
}
}
The REST Controller exposing the User based endpoints is as follows:
package com.hemant.userservice.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.PostConstruct;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.hemant.userservice.model.User;
@RestController
public class UserController {
private Map userMap;
@PostConstruct
public void initMap() {
userMap = new HashMap<>();
for (int i = 1; i <= 5; i++) { String uuid=UUID.randomUUID().toString(); userMap.put(uuid, new User(uuid, "User" + i, "user" + i + "@gmail.com" )); } } @RequestMapping(value="" , method=RequestMethod.GET) public List getAllUsers() {
return new ArrayList<>(userMap.values());
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUserById(@PathVariable("id") String id) {
User user = userMap.get(id);
if (null == user) {
throw new IllegalArgumentException("User not found for : " + id);
}
return user;
}
}
Here the mock data is created using @PostConstruct and 2 endpoints are provided
Get all Users
Get User by ID
3.2.4 Application properties
server :
port : 8080
spring :
application :
name : user-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
Explanation:
Spring.application.name : This is mandatory for all the Eureka Clients. This value does used as ServiceId by Eureka Server. Other services can access this service via using this ServiceId.
eureka.client.serviceUrl.defaultZone : This is the location of our Eureka server configured above.
Server.port - This web application will run on 8080 port.
3.3 Info Service
This is another Eureka Client service similar to the User Service.
Ho wever it provides additional endpoints which:
Get more information of the Eureka.
Consume the APIs of User service using Eureka.
The project structure is as follows:
3.3.1 Maven dependencies
4.0.0com.hemantinfo-service0.0.1-SNAPSHOTjarinfo-serviceDemo project for Spring Bootorg.springframework.bootspring-boot-starter-parent1.5.1.RELEASEUTF-8UTF-81.81.2.6.RELEASEorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testtestorg.springframework.cloudspring-cloud-starter-eureka${spring.cloud.version}org.springframework.bootspring-boot-maven-plugin
3.3.2 Spring boot application class
package com.hemant.info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class InfoServiceApp {
public static void main(String[] args) {
SpringApplication.run(InfoServiceApp.class, args);
}
}
3.3.3 Controllers
Here 2 controllers are provides
1. EurekaClientController : This provides information about the Eureka
package com.hemant.info.controller;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/eureka")
public class EurekaClientController {
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping(value = "", method = RequestMethod.GET)
public List getAllEurekaClients() {
return discoveryClient.getServices();
}
@RequestMapping(value = "/{serviceName}", method = RequestMethod.GET)
public Map getEurekaClientURLByName(@PathVariable String serviceName) {
List serviceInstances = discoveryClient.getInstances(serviceName);
if (serviceInstances.isEmpty()) {
throw new IllegalArgumentException("No instance found with serviceId :" + serviceName);
}
ServiceInstance instance = serviceInstances.get(0);
Map infoMap = new LinkedHashMap<>();
infoMap.put("host", instance.getHost());
infoMap.put("port", instance.getPort());
infoMap.put("uri", instance.getUri());
infoMap.put("serviceId", instance.getServiceId());
infoMap.put("secured", instance.isSecure());
return infoMap;
}
}
2. UserClientController : This consumes the User service’s endpoints.
The userService’sServiceId (spring.application.name in itsapplication.properties) i.e. “user-service” is used to query the DiscoverClient instance so as to fetch the instance of UserService. The only hard-coding here is serviceId. The other details like URL, PORT etc. is maintained by Eureka.
Spring.application.name : This is mandatory for all the Eureka Clients. This value does used as ServiceId by Eureka Server. Other services can access this service via using this ServiceId.
eureka.client.serviceUrl.defaultZone : This is the location of our Eureka server configured above.
Server.port - This web application will run on 8081 port.
4. Starting the microservices :
4.1 Starting Eureka Server
1. Start the Eureka Server application, go to the project’s root directory and execute
>>>mvn clean install
2. If the build is success, we can launch the application by:
2018-07-26 23:47:47.792 INFO 5715 --- [ main]
c.n.e.EurekaDiscoveryClientConfiguration : Updating port to 8761
2018-07-26 23:47:47.801 INFO 5715 --- [ main]
c.h.e.EurekaServerApplication : Started
EurekaServerApplication in 13.399 seconds (JVM running for 14.281)
You can access, the Eureka Server console at http://localhost:8761/
5. Presently no client services are started.
4.2 Starting User Service :
The steps for starting this Eureka Client service are:
1. Go to the project’s root directory and execute
>>>mvn clean install
2. If the build is success, we can launch the application by:
3. The following logs indicate that application is up and running and also registered with Eureka server.
2018-07-26 23:53:19.068 INFO 5752 --- [ main]
c.n.e.EurekaDiscoveryClientConfiguration : Updating port to 8080
2018-07-26 23:53:19.080 INFO 5752 --- [ main]
c.h.userservice.UserServiceApplication : Started
UserServiceApplication in 10.22 seconds (JVM running for 11.243)
2018-07-26 23:53:19.173 INFO 5752 --- [nfoReplicator-0]
com.netflix.discovery.DiscoveryClient : DiscoveryClient_USER-
SERVICE/192.168.1.117:user-service:8080 - registration status: 204
4. At the same point, in Eureka Server logs, you can see that User service has registered itself.
2018-07-26 23:53:19.165 INFO 5715 --- [nio-8761-exec-4]
c.n.e.registry.AbstractInstanceRegistry : Registered instance USER-
SERVICE/192.168.1.117:user-service:8080 with status UP (replication=false)
5. Upon refreshing the Eureka server console (http://localhost:8761/), you can see the UserService is now available.
4.3 Starting InfoService :
The steps are similar to that of starting of UserService. After starting Info Service, it should register with Eureka and Eureka console on refresh should reflect the same
2. Access the info-service endpoint to give information on the client services currently registered on Eureka Server.
GET http://localhost:8081/eureka
Response:
[
"user-service",
"info-service"
]
This gives the list of serviceIds (spring application names) which are registered with Eureka.
3. Access the API which provides more information on particular service, whose names is passed as path-variable
GET http://localhost:8081/eureka/user-service
Response
{
"host": "192.168.1.117",
"port": 8080,
"uri": "http://192.168.1.117:8080",
"serviceId": "USER-SERVICE",
"secured": false
}
GET http://localhost:8081/eureka/demo-service
Response
{
"timestamp": 1532630674705,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalArgumentException",
"message": "No instance found with serviceId :demo-service",
"path": "/eureka/demo-service"
}
Here since no service by serviceId "demo-service" is registered on Eureka, it's instance couldn't be found.
4. Access the UserClientController’s API. This will inturn fetch us the instance of UserService (running on 8080) from Eureka and call its API.
GET http://localhost:8081/user/6dc503a1-2f59-4712-aac2-e2cc5496f303
Response
{
"url": "http://192.168.1.117:8080/6dc503a1-2f59-4712-aac2-e2cc5496f303",
"user-service-response": "{\"id\":\"6dc503a1-2f59-4712-aac2-e2cc5496f303\",\"name\":\"User3\",\"email\":\"user3@gmail.com\"}"
}