Enable Javascript

Please enable Javascript to view website properly

Toll Free 1800 889 7020

Looking for an Expert Development Team? Take 2 weeks Free Trial! Try Now

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.

Webp.net-gifmaker

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.

Following are its salient features:

  • Github link - https://github.com/Netflix/eureka
  • 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:

Spring

3.1.1 Maven Dependencies

<?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.hemant</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-server</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-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>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>

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:

spring netflix

3.2.1 Maven Dependencies

<?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.hemant</groupId> <artifactId>user-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>user-service</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

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<String, User> 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<User> 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:

spring netflix

3.3.1 Maven dependencies

<?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.hemant</groupId> <artifactId>info-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>info-service</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring.cloud.version>1.2.6.RELEASE</spring.cloud.version> </properties> <dependencies> <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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>${spring.cloud.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

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<String> getAllEurekaClients() { return discoveryClient.getServices(); } @RequestMapping(value = "/{serviceName}", method = RequestMethod.GET) public Map<String, Object> getEurekaClientURLByName(@PathVariable String serviceName) { List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName); if (serviceInstances.isEmpty()) { throw new IllegalArgumentException("No instance found with serviceId :" + serviceName); } ServiceInstance instance = serviceInstances.get(0); Map<String, Object> 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.

package com.hemant.info.controller; import java.util.HashMap; 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.http.HttpMethod; import org.springframework.http.ResponseEntity; 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 org.springframework.web.client.RestTemplate; @RestController public class UserClientController { @Autowired private DiscoveryClient discoveryClient; private RestTemplate restTemplate = new RestTemplate(); private String userServiceName = "user-service"; @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public Map<String, String> getUserById(@PathVariable String id) { List<ServiceInstance> serviceInstances = discoveryClient.getInstances(userServiceName); if (serviceInstances.isEmpty()) { throw new IllegalArgumentException("No instance found with serviceId :" + userServiceName); } ServiceInstance userServiceInstance = serviceInstances.get(0); String url = userServiceInstance.getUri().toString() + "/" + id; ResponseEntity<String> authResponse = restTemplate.exchange(url, HttpMethod.GET, null, String.class); Map<String, String> map = new LinkedHashMap<>(); map.put("url", url); map.put("user-service-response", authResponse.getBody()); return map; } }

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.

3.3.4 Application properties

server.port : 8081 spring.application.name : info-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 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/

Spring netflix

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:

>>>java -jar target/user-service-0.0.1-SNAPSHOT.jar

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.

image009

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

image011

5. Demonstration

1. Access the User Service endpoints individually

GET http://localhost:8080/ Response: [ { "id": "30f9f24e-538b-458c-aa9b-1e06749ced52", "name": "User1", "email": "user1@gmail.com" }, { "id": "b514af00-2d32-4fe6-b9c1-43aeb83d58d4", "name": "User2", "email": "user2@gmail.com" }, { "id": "2ab01121-0b71-4581-a127-ad46c20f67a3", "name": "User4", "email": "user4@gmail.com" }, { "id": "d994c3ce-153f-4eaf-9aca-f2ae9a3735d3", "name": "User5", "email": "user5@gmail.com" }, { "id": "6dc503a1-2f59-4712-aac2-e2cc5496f303", "name": "User3", "email": "user3@gmail.com" } ] --------------------------------------------------------- GET http://localhost:8080/6dc503a1-2f59-4712-aac2-e2cc5496f303 Response: { "id": "6dc503a1-2f59-4712-aac2-e2cc5496f303", "name": "User3", "email": "user3@gmail.com" }

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\"}" }

Conclusion:

Thus we have implemented the Service registry and Discovery using Spring Cloud Netflix.

  • Implementation of Eureka Server
  • Registered the client services with Eureka
  • Also used Eureka to access other services by getting its instance details by just using its service ID.
Software Development Team
Need Software Development Team?
captcha
🙌

Thank you!
We will contact soon.

Oops! Something went wrong.

Recent Blogs

Categories

NSS Note
Trusted by Global Clients