DEV Community

Sidd B
Sidd B

Posted on • Originally published at bootng.com on

Microservices Registration and Discovery using Spring Cloud and Eureka


This blog post drives though an implementation pattern and a working setup with multiple microservices where they get registered with Eureka and the client can discover them and invoke them

Table of Content

Microservices Registration and Discovery using Spring Cloud, Eureka

This blog post drives though an implementation pattern and a working setup with multiple microservices where they get registered with Eureka and the client can discover them and invoke them

What is Eureka

Eureka is a REST-based service that provides support for discovering other services so that clients can use the Eureka service to discover services and calling those services.

Why we need Discovery Service

In a Microservices architecture where we have multiple services running, we need a mechanism to know what are the services and how to invoke them. Each service typically will have a different URL or IP address to reach out to. Also, clients of those microservices need to know when a new

microservice is added or an existing microservice was taken down for maintenance. Discovery service provides that mechanism, where services can register themself in the discovery service and clients, can query the discovery service to know more about the available microservices.

Project achievement

In this project, we will go through an end to end use case where we will have a client microservice calling another microservice utilizing Eureka discovery service. Essentially we want to find existing services by looking up in discovery service and then invoking the target service without knowing about their whereabouts like URL, or IP or port, etc. For that, we will build a discovery server one service microservice and one client microservice.

Project Contents

  • Eureka Discovery Service: Provides the registration and discovery
  • Article Microservice: Provides REST services for Article CRUD operation
  • Client Microservice: Invokes the article microservice

Above pictures show what we are going to achieve in very high level

1) We will deploy a Discovery service

2) We will deploy Article Service which registers itself with discovery service with a unique name.

3) We will deploy a consumer application which is another spring boot application which will invoke the

 Article Service by its name.  
Enter fullscreen mode Exit fullscreen mode

Technology Used

Java 11

Apache Maven 3.5.0

Spring Boot 2.2.6

Spring Cloud Hoxton.SR1

Netflix Ribbon 2.3.0

Open Feign

JSON

Discovery Server

The following section highlights the important code related to Eureka Server. The details can be looked into in the git repo. Mainly we need to add the maven dependency for Eureka Discovery Server, in the spring boot properties file add the server port and defaultZone and annotate the SpringBoot Application class with @EnableEurekaServer.

We need to include the Netflix Eureka Server dependency in the maven pom file.

pom.xml

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

application.properties

server.port=8761##Eureka Relatedeureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/eureka.client.healthcheck.enabled=true
Enter fullscreen mode Exit fullscreen mode

The Eureka Discovery Server is a Spring Boot application that is very simple. We need to annotate the application with @EnableEurekaServer. @EnableEurekaServer allows this application to run as a Eureka server, aided by Spring Boot to instantiate Eureka-related beans based on configuration properties.

ServiceDiscovery.java

package com.bootng.service;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer@SpringBootApplicationpublic class ServiceDiscovery { public static void main(String[] args) { SpringApplication.run(ServiceDiscovery.class, args); }}
Enter fullscreen mode Exit fullscreen mode

Now when we build and start the discovery server and access the server through http://localhost:8761 we would see something like bellow. Notice the following screenshots shows no entries under applications because no other services are up yet.

| |
| Discovery Service UI

|

Article Service

Now we will build and deploy the Article microservice which is another spring boot application that registers itself with the discovery service. The name with which this service is registered is "article-service" and is defined in bootstrap.properties file.

pom.xml

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
Enter fullscreen mode Exit fullscreen mode

application.properties

## Server Relatedserver.port=9051## Eureka Relatedeureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/eureka.client.register-with-eureka=trueeureka.client.fetch-registry=falseeureka.vipAddress=article-service
Enter fullscreen mode Exit fullscreen mode

bootstrap.properties

spring.application.name=article-service
Enter fullscreen mode Exit fullscreen mode

Following is the main application class representing Article Microservice.

ArticleMicroservice

@ComponentScan({ "com.siddb" })@SpringBootApplication@EnableEurekaClient@EnableDiscoveryClientpublic class ArticleMicroservice {    public static void main(String args[]) { SpringApplication.run(ArticleMicroservice.class, args);    }}
Enter fullscreen mode Exit fullscreen mode

This Spring Boot Application exposes the following REST endpoints through the ArticleController.java class. And registers itself with the service discovery with the name "article-service"

ArticleController.java

@RestController@RequestMapping("/api")public class ArticleController { private static final Logger log = LoggerFactory.getLogger(ArticleController.class); @Autowired ArticleService articleService; @RequestMapping(value = {"/articles"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<Article>> getArticles() throws AppException { List<Article> articles = articleService.getArticles(); return new ResponseEntity<List<Article>>(articles, HttpStatus.OK); } @RequestMapping(value = {"/articles/{id}"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Article> getArticle(@PathVariable(value = "") String id) throws AppException, NotFoundException { Article articles = articleService.getByID(id); return new ResponseEntity<Article>(articles, HttpStatus.OK); } //other methods for POST and Deleted removed from here for readability.}
Enter fullscreen mode Exit fullscreen mode

REST Endpoints

Sample Response

GET http://localhost:9051/api/articles

[{ "id": "America-Travel", "name": "America Travel", "description": "Places to travel in AmericaLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "category": "Travel" }]
Enter fullscreen mode Exit fullscreen mode

GET http://localhost:9051/api/articles

Sample Response: [{ "id": "America-Travel", "name": "America Travel", "description": "Places to travel in AmericaLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "category": "Travel" }]

[{ "id": "America-Travel", "name": "America Travel", "description": "Places to travel in AmericaLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "category": "Travel" }]

Article Client Service

Now we will build and deploy the Client Service application which is another Spring Boot application that consumes the REST services deployed by Article Service. Most interesting part

pom.xml

<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-loadbalancer</artifactId>    </dependency>   <!--ribbon related -->  <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>  </dependency>   <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId>   </dependency>   <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId>   </dependency></dependencies>
Enter fullscreen mode Exit fullscreen mode

application.properties

hostname=localhostlogging.level.org.springframework=infologging.level.root=infospring.main.banner-mode=offserver.port=9053##Managementmanagement.endpoint.health.show-detailsmanagement.health.key.ping=enabledmanagement.endpoints.web.exposure.include=info,health,mappings##Eureka Relatedeureka.instance.leaseRenewalIntervalInSeconds=1eureka.instance.leaseExpirationDurationInSeconds=90eureka.instance.hostname=${hostname}eureka.instance.hostname.metadataMap.instanceId=${spring.application.name}:${server.port}eureka.client.register-with-eureka=trueeureka.client.fetch-registry=trueeureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/eureka.client.healthcheck.enabled=true##Eureka Ribbonarticle-service-consumer.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerListarticle-service-consumer.ribbon.DeploymentContextBasedVipAddresses=article-service
Enter fullscreen mode Exit fullscreen mode

Following is the Consumer Application Class. We have annotated it with @EnableDiscoveryClient so that it can discover other services registered with the Discovery Server.

ConsumerApplication

@ComponentScan({"com.siddb", "com.siddb.controller"})@SpringBootApplication@EnableDiscoveryClientpublic class ConsumerApplication { public static void main(String args[]) { SpringApplication.run(ConsumerApplication.class, args); }}
Enter fullscreen mode Exit fullscreen mode

Following is the main controller which uses the discovery service internally and invokes services on the service article-service using ribbon. From the ribbon configuration in (application.properties), it knows article-service-consumer is tied to a service with VIP Address (name) article-service.

RibbonController.java

@RestControllerpublic class RibbonController { @Autowired private RestTemplate restTemplate; /** * This method calls the microservice GET article-service/api/articles using Ribbon to get a list of * Articles and returns the same. it uses RestTemplate to make the API calls. * @return * @throws Exception */ @RequestMapping(value = {"render"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<String> renderArticles() throws Exception { JsonNode repos = restTemplate.getForObject("http://article-service-consumer/api/articles", JsonNode.class); int counter = 1; StringBuilder result = new StringBuilder("\n List of Articles"); if (repos.isArray()) { for (JsonNode jsonNode : repos) { result.append("\n Repo ").append(counter++).append("::"); result.append(jsonNode.get("name").asText()); } } return new ResponseEntity<String>(result.toString(), HttpStatus.OK); }}
Enter fullscreen mode Exit fullscreen mode

The Consumer Controller uses the ribbon configuration that we have added in the property file to invoke the article service by the ribbon name "article-service-consumer". This is where the Consumer controller will discover the actual API for the corresponding service name. And call GET http://article-service-consumer/api/articles will be actually to http://localhost:9051/api/articles. This is how using the Eureka Discovery service the client becomes independent of the service (IP address, port, etc.). The client simply can refer to the service with its published name.

Start Microservices

Now the fun part, start all the microservices and invoke the /render REST API. We need to start all services (discovery service, article service, consumer service). For that, we first need to check out the repo from git and build/run each project.

1) git clone https://github.com/siddharthagit/spring-boot-sdc.

2) go to the root folder that contains this repo.

3) open 3 Terminals one for each subproject (eureka-service-discovery, ms-article-service, ms-consumer-service).

4) build each project and start the services.

mvn clean install

mvn spring-boot:run

5) Check the status of each service on the discovery server http://localhost:8761

6)Invoke the REST API on consumer service http://localhost:9053/render

This API internally will use the discovery server to resolve the service and will route the call to http://localhost:9051/api/articles.

http://localhost:9053/render

List of Articles Repo 1::America Travel Repo 2::Asia Travel Repo 3::Europe Travel Repo 4::Road Trip Repo 5::Winter Travel Repo 6::Japan Travel

| |
| Discovery Server UI with all services running

|

Conclusion

In this article, we've covered how to use Spring Cloud Eureka for service discovery in the microservice/cloud environment. We created two simple REST services that communicate with each other without hardcoding any hostname/port while making REST calls. Though in this article we have shown how the client can invoke the article service with ribbon, the git repo also contains a Feign client that uses Feign and will be covered in a different article.

References

Git Repo

Spring Cloud Eureka

Discussion (0)