How to Develop Microservices With Spring Cloud and Netflix Discovery

Today I am going to provide you an example of a small microservices based application, where I am going to implement a Eureka discovery server to register all the microservices application in it, so that each of these services can be accessible from all the microservices applications registered with Discovery Server. 

For this example, we are going to create total four microservices applications/services. 

  1. discovery-service – A eureka Discovery server.
  2. library-service ( a rest service, a discovery client as well).
  3. book-service (a rest service, a discovery client as well).
  4. read-service – A rest service which will consume both book-service and library-library service. This is also a discovery client which will register itself in discovery server. 

Here, the role of Discovery Server is, to register all the client applications in it so that their information can be globally available to all the registered client applications. So this Discovery Server will allow microservices to consume other microservices without knowing their exact location i.e. their host id and port number. Even if you decide to move an application/service to a different server, no application previously consuming your service will be impacted. 

Because from discovery server they will know the new location (new host id and port) of your service which has been moved to a different server. Suppose you have decided to move your application from an Indian server (http://myindia.gov.in:9001) to an US server (http://your-us.gov.us:99013) you do not need to inform any of the applications previously consuming your services. They will get to know it automatically from discovery server and they even do not need to do anything at their end. 

Please see the below diagram to understand how a discovery server serve a client application registered in it. For example read-service need to consume services from book-service and library-service, now how will read-service get the host name and port number of both these application to consume? It will be provided by discovery service as both book-service and library-service already registered themselves in discovery-service.

Eureka Discovery Server

To read more about Spring cloud Netflix you can visit https://cloud.spring.io/spring-cloud-netflix/2.0.x/single/spring-cloud-netflix.html.

Let us develop the entire project step-by-step.

Step 1

First, we will create discovery-service which is a Eureka discovery server. If you are new to Spring Boot, you can follow my article https://github.com/prateekparallel/spring-boot-rest-2 to create a Spring Boot application. Now, click https://start.spring.io/ and open Spring Initializr. Now do exactly what I am doing in the below screen shot. (Group, Artifact, etc fill as per your choice):

Spring initializr

Click ADD DEPENDENCIES and select all the dependencies (in search bar just type the dependency – Eureka and Actuator and select it one at a time. Do not select Eureka client)

Adding dependencies

Now click the GENERATE button to generate the project and save it to your machine and import it as maven project to your tool (here I am using Eclipse).

In eclipse right click on project explorer and select import and select -

Creating Java project


Now, open the Java file, DemoDiscoveryServerApplication.java, and add @EnableEurekaServer in it:

Java
 




xxxxxxxxxx
1
15


 
1
package com.bhaiti.jorhat.demodiscoveryserver;
2
 
          
3
import org.springframework.boot.SpringApplication;
4
import org.springframework.boot.autoconfigure.SpringBootApplication;
5
 
          
6
@SpringBootApplication
7
@EnableEurekaServer
8
public class DemoDiscoveryServerApplication {
9
 
          
10
 public static void main(String[] args) {
11
  SpringApplication.run(DemoDiscoveryServerApplication.class, args);
12
 }
13
 
          
14
}
15
 
          



Now, rename your application.properties  file to application.yml and add the following:

YAML
 




xxxxxxxxxx
1
13


 
1
server.port9001
2
 
          
3
spring.application.namedemo-discovery-server
4
 
          
5
eureka:
6
 server:
7
 evictionIntervalTimerInMs3000
8
 response-cache-update-interval-ms3000
9
 
          
10
 client:
11
 registerWithEurekafalse
12
 fetchRegistryfalse
13
 service-url.defaultZonehttp://localhost:${server.port}/eureka



You can see above we assigned false to both registerWithEureka and fetchRegistry. The reason is we do not want to register this application in itself as this is a discovery server to register discovery client only. For detail about configuration please check in this repository

Your discovery server is ready now. Just compile it and run it in command prompt.

In the project home directory just execute the below command:

mvnw clean package

Now, start the discovery server

java -jar target/demo-discovery-server-0.0.1-SNAPSHOT.jar

<>now you can check the dashboard of Eureka discovery server by hitting http://localhost:9001


Now you do not see any client in it. Let’s develop our first client application book-service

Step 2: Create a REST service application call book-service. To create the project , follow below steps

Please notice here we have selected dependency Eureka Client this time, as our REST service application is also a Eureka Client as well. Generate the project and import to your eclipse as maven project.

Now change the application.property to application.yml and modify it like below-

YAML
 




xxxxxxxxxx
1
12


 
1
server.port9901
2
 
          
3
spring:
4
 application.namebook-service 
5
 
          
6
eureka:
7
 client:
8
 serviceUrl:
9
 defaultZonehttp://localhost:9001/eureka/
10
 registryFetchIntervalSeconds1
11
 instance:
12
 leaseRenewalIntervalInSeconds1



Now in the above you can see that defaultZone is assigned with the address of our discovery server. So this discovery client will register itself in the discovery server which will be listing at http://localhost:9001. Now we have to do some coding in our REST application book-service. First open BookServiceApplication.java file and add @EnableDiscoveryClient in it to activate the discovery client.

Java
 




xxxxxxxxxx
1
16


 
1
package com.bhaiti.jorhat.book.bookservice;
2
 
          
3
import org.springframework.boot.SpringApplication;
4
import org.springframework.boot.autoconfigure.SpringBootApplication;
5
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6
 
          
7
@SpringBootApplication
8
@EnableDiscoveryClient
9
public class BookServiceApplication {
10
 
          
11
 public static void main(String[] args) {
12
  SpringApplication.run(BookServiceApplication.class, args);
13
 }
14
 
          
15
}
16
 
          



Now create a new java class call BookService.java like below

And add the following code in it

Java
 




xxxxxxxxxx
1
14


 
1
package com.bhaiti.jorhat.book.bookservice;
2
 
          
3
import java.util.Arrays;
4
import java.util.List;
5
 
          
6
public class BookService { 
7
 private static List<String> bookList = Arrays.asList("Book1","Book2","Book3");
8
 
          
9
 public static List<String> getBookList() {
10
  return bookList;
11
 }
12
 
          
13
}
14
 
          



Now create a controller class like below:-

Java
 




xxxxxxxxxx
1
18


 
1
package com.bhaiti.jorhat.book.bookservice;
2
 
          
3
import java.util.List;
4
 
          
5
import org.springframework.http.ResponseEntity;
6
import org.springframework.web.bind.annotation.GetMapping;
7
import org.springframework.web.bind.annotation.RestController;
8
 
          
9
@RestController
10
public class BookController {
11
 
          
12
 @GetMapping("/books")
13
 public ResponseEntity<List<String>> getBookList(){ 
14
  return ResponseEntity.ok(BookService.getBookList()); 
15
 }
16
 
17
}
18
 
          



Add the below dependency in your pom.xml file

XML
 




xxxxxxxxxx
1
12


 
1
<dependency>
2
 <groupId>org.springframework</groupId>
3
  <artifactId>spring-web</artifactId>
4
</dependency>
5
<dependency>
6
 <groupId>org.springframework.boot</groupId> 
7
  <artifactId>spring-boot-starter-web</artifactId>
8
</dependency>
9
<dependency>
10
 <groupId>org.springframework.boot</groupId>
11
 <artifactId>spring-boot-starter</artifactId>
12
</dependency>



Now compile it and run it like below:-

Shell
 




xxxxxxxxxx
1


 
1
cd book-service
2
mvnw clean package
3
java -jar target/book-service-0.0.1-SNAPSHOT.jar


 

Now refresh eureka (discovery server) dashboard, you can see that book-service has been registered in it. 

Step 3: Now create library-service application. Just repeat step 2 and create a project and import it to you development area.


Now update your pom.xml exactly like you did for book-service.

Now add two classes respectively LibraryService.java and LibraryController.java

First modify your LibraryServiceApplication.java and add @EnableDiscoveryClient 

Java
 




xxxxxxxxxx
1
10


 
1
@SpringBootApplication
2
@EnableDiscoveryClient
3
public class LibraryServiceApplication {
4
 
          
5
 public static void main(String[] args) {
6
  SpringApplication.run(LibraryServiceApplication.class, args);
7
 }
8
 
          
9
}
10
 
          



Now create LibraryService.java and modify it like below

Java
 




xxxxxxxxxx
1
22


 
1
package com.bhaiti.uk.global.repo;
2
 
          
3
import java.util.HashMap;
4
import java.util.Map;
5
 
          
6
public class LibraryService {
7
 
8
 private static Map<String, String> libList;
9
 
10
 static {
11
  libList = new HashMap<String, String>();
12
  libList.put("Book1", "Library3");
13
  libList.put("Book2", "Library1");
14
  libList.put("Book3", "Library2");
15
 }
16
 
          
17
 public static Map<String, String> getLibList() {
18
  return libList;
19
 }
20
 
          
21
}
22
 
          



Now create LibraryController.java and modify it like below

Java
 




xxxxxxxxxx
1
18


 
1
package com.bhaiti.uk.global.repo;
2
 
3
import java.util.Map;
4
 
5
import org.springframework.http.ResponseEntity;
6
import org.springframework.web.bind.annotation.GetMapping;
7
import org.springframework.web.bind.annotation.RestController;
8
 
9
 
10
@RestController
11
public class LibraryController {
12
 
13
       @GetMapping("/librarys")
14
       public ResponseEntity<Map<String, String>> getBookList(){ 
15
              return ResponseEntity.ok(LibraryService.getLibList()); 
16
       }
17
       
18
}



Now, rename your application.properties file to application.yml and add the following:

YAML
 




xxxxxxxxxx
1
12


 
1
server.port9902
2
 
          
3
spring:
4
 application.namelibrary-service 
5
 
          
6
eureka:
7
 client:
8
 serviceUrl:
9
 defaultZone${EUREKA_URI:http://localhost:9001/eureka}
10
 registryFetchIntervalSeconds1
11
 instance:
12
 leaseRenewalIntervalInSeconds1


 

Compile and run the application:

Shell
 




xxxxxxxxxx
1


 
1
cd library-service
2
mvnw clean package
3
java -jar target/library-service-0.0.1-SNAPSHOT.jar



Now, refresh the Eureka dashboard, and you can see that your library-service also listed in it.

Running Eureka dashboard

Step 5: Now we are going to create our last REST service - read-service. Now create it and import it to your eclipse. Please notice we are adding one more dependency here i.e. OpenFeign.

Creating new Maven project

Now change the name of file application.properties file to application.yml and add the following:

YAML
 




xxxxxxxxxx
1
12


 
1
server.port9903
2
 
          
3
spring:
4
 application.namelibrary-service 
5
 
          
6
eureka:
7
 client:
8
 serviceUrl:
9
 defaultZone${EUREKA_URI:http://localhost:9001/eureka}
10
 registryFetchIntervalSeconds1
11
 instance:
12
 leaseRenewalIntervalInSeconds1


 

Modify your pom.xml exactly the same way as you did for library-service.

Here, in read-service, we are going to consume both book-service and library-service. To call the REST service API of library-service and book-service we will implement a Feign Client here.

First modify your ReadServiceApplication.java like below:

Java
 




xxxxxxxxxx
1


 
1
@SpringBootApplication
2
@EnableDiscoveryClient
3
public class ReadServiceApplication {


 

Now, create our Feign Clients here. Create two interface respectively BookClient.java and LibraryClient.java, and modify them like below:

Java
 




xxxxxxxxxx
1
16


 
1
package com.bhaiti.jorhat.read;
2
 
          
3
import java.util.List;
4
 
          
5
import org.springframework.cloud.openfeign.FeignClient;
6
import org.springframework.web.bind.annotation.GetMapping;
7
 
          
8
 
          
9
@FeignClient("book-service")
10
public interface BookClient {
11
 
12
 @GetMapping("/books")
13
  List<String> getBookList(); 
14
 
15
}
16
 
          



Java
 




xxxxxxxxxx
1
13


 
1
package com.bhaiti.jorhat.read;
2
 
          
3
import java.util.Map;
4
 
          
5
import org.springframework.cloud.openfeign.FeignClient;
6
import org.springframework.web.bind.annotation.GetMapping;
7
 
          
8
@FeignClient("library-service")
9
public interface LibraryClient {
10
 @GetMapping("/librarys")
11
    Map<String, String> getLibraryList();
12
}
13
 
          



The beauty of using FeignClient is that you don’t need to know the host name and port number of the application where you are going to make a REST call. Here to make a REST call to book-service we are not using an URI. Feign use Ribbon to get everything requires for the REST call and gets all the detail about book-service and library-service(host and port) from discovery server. Because book-service and library-service are not only a REST based application but also a discovery client too, hence it can retrieve information about other clients of discovery server. 

To read more about FeignClient visit https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html and https://cloud.spring.io/spring-cloud-openfeign/reference/html/

Suppose you don’t want to use a FeignClient and want to make a REST call with http connection object or with RestTemplate, in such situation you can use the below codes to get the application’s host name and port number.

Java
 




xxxxxxxxxx
1
12


 
1
@Autowired
2
private EurekaClient eurekaClient;
3
 
          
4
public String getBaseUrl() {
5
 Application application = eurekaClient.getApplication("book-service");
6
 InstanceInfo instanceInfo = application.getInstances().get(0);
7
  
8
 String hostname = instanceInfo.getHostName();
9
 int port = instanceInfo.getPort();
10
 
          
11
 return "http://" + hostname + ":" + port;
12
}


 

(Above codes are out of the scope of this article. I am discussing about it in my article SSL based/Secured FeignClient…) 

Now add the below controller 

Java
 




xxxxxxxxxx
1
40


 
1
package com.bhaiti.jorhat.read;
2
 
          
3
 
          
4
import java.util.List;
5
import java.util.Map;
6
 
          
7
import org.springframework.beans.factory.annotation.Autowired;
8
import org.springframework.http.ResponseEntity;
9
import org.springframework.web.bind.annotation.GetMapping;
10
import org.springframework.web.bind.annotation.PathVariable;
11
import org.springframework.web.bind.annotation.RestController;
12
 
          
13
 
          
14
@RestController
15
public class ReadServiceController {
16
 
17
 @Autowired
18
 BookClient bookClient;
19
 
20
 @Autowired
21
 LibraryClient libraryClient;
22
 
          
23
 @GetMapping(path= "/read/{bookName}", produces = "application/json")
24
 public ResponseEntity<String> getReadingLocation(@PathVariable(value = "bookName") String bookName){
25
  String response;
26
  
27
  List<String> bookList = bookClient.getBookList(); 
28
  if(bookList.contains(bookName)) {
29
   Map<String, String> libList = libraryClient.getLibraryList();
30
   response = "You can read this book - " + bookName 
31
     + " at this Library - " + libList.get(bookName);
32
  }
33
  else {
34
   response = "Your Book - " + bookName + " is not currently available in Libaries";
35
  }
36
  return ResponseEntity.ok().body(response); 
37
 }
38
}
39
 
          
40
 
          



And finally modify the ReadServiceApplication.java like below:

Java
 




xxxxxxxxxx
1
18


 
1
package com.bhaiti.jorhat.read;
2
 
          
3
import org.springframework.boot.SpringApplication;
4
import org.springframework.boot.autoconfigure.SpringBootApplication;
5
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6
import org.springframework.cloud.openfeign.EnableFeignClients;
7
 
          
8
@SpringBootApplication
9
@EnableFeignClients
10
@EnableDiscoveryClient
11
public class ReadServiceApplication {
12
 
          
13
 public static void main(String[] args) {
14
  SpringApplication.run(ReadServiceApplication.class, args);
15
 }
16
 
          
17
}
18
 
          



Now, your microservices application is fully ready for testing. 

First compile it and run it and check the Eureka server.

Shell
 




x


 
1
cd read-service
2
mvnw clean package
3
java -jar target/read-service-0.0.1-SNAPSHOT.jar



Now, hit the below URL and check

Checking local URL

It will display the below line in your browser.

Just change the name of the book to Book1 and Book2 and Book4 and test for each entrySource code of this project is available here - https://github.com/prateekparallel/microservices-example.

Hope you enjoyed this tutorial.

 

 

 

 

Top