Learn to create Microservices, based on Spring cloud, registering on HashiCorp Consul registry server and how other microservices (discovery clients) use it to register and discover services to call their APIs.

We will be using Spring Boot based Spring Cloud API. We will use Consul registry server for building the service registry server and generic discovery clients which will register themselves and discover other services to call REST APIs.

Table of Contents

Overview
Configuring Consul in Local workstation
Student Service
School Service - Discovery Client
Demo
Things to check if facing any error
Summary

Overview

Consul provides multiple features like service discovery, configuration management, health checking and key-value store etc. Today we will concentrate on service registry and discovery part. We will develop the below components to build a distributed Eco system where each component is somehow dependent on each other, yet they are very much loosely coupled and of course fault tolerance.

  • Consul Agent – running on localhost acting as discovery/registry server functionality.
  • Student Microservice – which will give some functionality based on Student entity. It will be a rest based service and most importantly it will be a discovery service client, which will talk with Consul Server/Agent to register itself in the service registry.
  • School Microservice – Same type as of Student service – only added feature is that it will invoke Student service with service look up mechanism. We will not use absolute URL of student service to interact with that service. We will use Consul discovery feature and use that to look up student service instance before invoking that.

Here is the overall component interaction diagram for the same.

Component Diagram

Tech Stack and Runtime

  • Java 1.8
  • Eclipse IDE
  • Consul as Service Registry Server
  • Spring cloud
  • Spring boot
  • Spring Rest
  • Maven

Configuring Consul in Local workstation

Before starting the exercise, We need to first download, configure and run consul agent in localhost.

  • Download from Consul portal. Choose particular package based on the operating System. Once downloaded the zip, we need to unzip it to desired place.
  • Start Consul Agent in local workstation – The Zip file that we have unzipped, has only one exe file called consul.exe. We will start a command prompt here and use below command to start the agent.
    consul agent -server -bootstrap-expect=1 -data-dir=consul-data -ui -bind=192.168.6.1

    Make sure you enter the correct bind address, it would be different depending on the LAN settings. Do a ipconfig in command prompt to know your IpV4 address and use it here.

    ipconfig command

    Agent Start Command Log
  • Test whether Consul Server is running – Consul runs on default port and once agent started successfully, browse http://localhost:8500/ui and you should see a console screen like –

    Consul console – No service registered

So we have configured consul in our local machine and consul agent is running successfully. Now we need to create clients and test the service registry and discovery part.

Student Service

Follow these steps to create and run student service. It will be a discovery client registering itself to consul service that is already running in our machine right now.

Create Student Project

Create a Spring boot project from initializer portal with four dependencies i.e.

  • Actuator
  • Web
  • Rest Repositories
  • Consul Discovery

Give other maven GAV coordinates and download the project.

student service project generation

Unzip and import the project into Eclipse as existing maven project.

Now add the @org.springframework.cloud.client.discovery.EnableDiscoveryClient annotation on Spring boot application class present in src folder. With this annotation, this artifact will act like a spring discovery client and will register itself in the consul server attached to this service.

package com.example.howtodoinjava.springcloudconsulstudent;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudConsulStudentApplication {

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

Service Configuration

Open application.properties and add below properties

server.port=9098
spring.application.name: student-service
management.security.enabled=false

Here is the each property details –

  • server.port=9098 – will start the service in default 9098 port.
  • spring.application.name: student-service – will registers itself in consul server using student-service tag and also other services will lookup this service with this name itself.
  • management.security.enabled=false – is not actually required for this exercise, but it will disable spring security in the management endpoints provided by actuator module.

Add REST APIs

Now add one RestController and expose one rest endpoint for getting all the student details for a particular school. Here we are exposing /getStudentDetailsForSchool/{schoolname} endpoint to serve the business purpose. For simplicity, we are hard coding the student details.

package com.example.howtodoinjava.springcloudconsulstudent.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.example.howtodoinjava.springcloudconsulstudent.domain.Student;

@RestController
public class StudentServiceController {

	private static Map<String, List<Student>> schooDB = new HashMap<String, List<Student>>();

	static {
		schooDB = new HashMap<String, List<Student>>();

		List<Student> lst = new ArrayList<Student>();
		Student std = new Student("Sajal", "Class IV");
		lst.add(std);
		std = new Student("Lokesh", "Class V");
		lst.add(std);

		schooDB.put("abcschool", lst);

		lst = new ArrayList<Student>();
		std = new Student("Kajal", "Class III");
		lst.add(std);
		std = new Student("Sukesh", "Class VI");
		lst.add(std);

		schooDB.put("xyzschool", lst);

	}

	@RequestMapping(value = "/getStudentDetailsForSchool/{schoolname}", method = RequestMethod.GET)
	public List<Student> getStudents(@PathVariable String schoolname) {
		System.out.println("Getting Student details for " + schoolname);

		List<Student> studentList = schooDB.get(schoolname);
		if (studentList == null) {
			studentList = new ArrayList<Student>();
			Student std = new Student("Not Found", "N/A");
			studentList.add(std);
		}
		return studentList;
	}
}

Student.java model

package com.example.howtodoinjava.springcloudconsulstudent.domain;

public class Student {
	private String name;
	private String className;

	
	public Student(String name, String className) {
		super();
		this.name = name;
		this.className = className;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}
}

Verify Student Service

Start this project as spring boot application. Now verify that this service has been registered in Consul server automatically. Go to Consul Agent console and refresh the page. Now if everything goes well, we will see one entry for student-service in the Consul Agent console.

Consul console Student Service registered

This indicates that both Consul server and client are aware each other and this is kind of auto registering and discovery happening among consul server and student service.

We will now verify that the /getStudentDetailsForSchool/{schoolname} endpoint is up and running. Go to browser and go to http://localhost:9098/getStudentDetailsForSchool/abcschool, it will give the Student details for a particular school abcschool.

Student Service Response

School Service – Discovery Client

Now we will create school service which will register itself with consul server – and it will discover and invoke student service without hardcoded URL path.

Follow the same steps to create and run school service. It will be a discovery client registering itself to consul service that is already running in our machine right now.

It will internally call student service that is already developed and it will use consul service discovery feature to discover the student instance.

Create School Project

Create a Spring boot project from initializer portal with four dependencies i.e.

  • Actuator
  • Web
  • Rest Repositories
  • Consul Discovery

Give other maven GAV coordinates and download the project.

School Generation

Unzip and import the project into Eclipse as existing maven project.

Now add the @org.springframework.cloud.client.discovery.EnableDiscoveryClient annotation on Spring boot application class present in src folder. With this annotation, this artifact will act like a spring discovery client and will register itself in the consul server attached to this service.

package com.example.howtodoinjava.springcloudconsulschool;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudConsulSchoolApplication {

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

Service Configuration

Open application.properties and add below properties

server.port=8098
spring.application.name: school-service
management.security.enabled=false

Here is the each property details –

  • server.port=8098 – will start the service in default 8098 port
  • spring.application.name: school-service – will registers itself in consul server using school-service tag.
  • management.security.enabled=false – will disable spring security in the management endpoints provided by actuator module.

Add REST API which consume student service’s REST API

Now add one RestController and expose one rest endpoint for getting school details. This endpoint will use the service discovery style URL using the application name, instead full URL convention with host:port.

SchoolServiceController.java

package com.example.howtodoinjava.springcloudconsulschool.controller;

import org.springframework.beans.factory.annotation.Autowired;
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.example.howtodoinjava.springcloudconsulschool.delegate.StudentServiceDelegate;

@RestController
public class SchoolServiceController {
	
	@Autowired
	StudentServiceDelegate studentServiceDelegate;

	@RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
	public String getStudents(@PathVariable String schoolname) 
	{
		System.out.println("Going to call student service to get data!");
		return studentServiceDelegate.callStudentServiceAndGetData(schoolname);
	}
}

StudentServiceDelegate.java

package com.example.howtodoinjava.springcloudconsulschool.delegate;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class StudentServiceDelegate 
{
	@Autowired
	RestTemplate restTemplate;
	
	public String callStudentServiceAndGetData(String schoolname) 
	{
		System.out.println("Consul Demo - Getting School details for " + schoolname);

		String response = restTemplate.exchange("http://student-service/getStudentDetailsForSchool/{schoolname}", 										HttpMethod.GET, null, new ParameterizedTypeReference<String>() {}, 												schoolname).getBody();
		
		System.out.println("Response Received as " + response + " -  " + new Date());

		return "School Name -  " + schoolname + " :::  Student Details " + response + " -  " + new Date();
	}
	
	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

Look at above code. In StudentServiceDelegate, we have used RestTemplate to invoke student service and used URL of student service as http://student-service/getStudentDetailsForSchool/{schoolname}.

This way we can get rid of specific service configuration and we can give the service look up responsibility to consul server and rest template provided here. We can also apply load balancing (see @LoadBalanced annotation) here if the multiple instances are running for the same service.

Demo

Do the following steps one by one to understand the whole thing –

  • Check Consul Agent is still running – Open browser and browse http://localhost:8500/ui. It should display consul console as described above.
  • Check Student Service is already running – Check from both consul admin page and browser that student service is up and running. If not start that service and verify that it got registered in consul server.
  • Start and Check School Service – Start school service from command prompt and check that it got registered in consul server.
  • Open browser and test school REST service using URL http://localhost:8098//getSchoolDetails/abcschool. It will give below response and it will internally invoke student service using consul service.

    School Service Response
  • Also Try starting multiple instance Student Service by changing port by java -jar "-Dserver.port=9099 target\spring-cloud-consul-student-0.0.1-SNAPSHOT.jar. Those will also be registered in consul and as we are using @LoadBalanced annotation in the RestTemplate, load balancing will also be done internally. Check the respective student service console to verify which instance got invoked in multi-instance scenario.

    This how consul server will look like once we have multi service and multiple instance registered.

    Consul All Services Running

Things to check if facing any error

  • Annotation @EnableDiscoveryClient and Consul agent running are the heart of the application ecosystem. Without those two things will not work at all.
  • Make sure at the time of starting school service, student service, consul server Agent is already running Otherwise it might take some time to register and which might confusion while testing.

Summary

We saw how easily one can deploy consul service registry and discovery server as well as clients efficiently. Spring framework is maintaining lots of things internally. Here we are just using couple of annotations and very minimal configuration to achieve the whole things quickly.

That’s all about creating spring consul server and service registration for microservices.

Please add comments if you have any difficulty executing this article. We will be happy to look into the problem.

Happy Learning !!