Working with Spring Cloud Consul

Using Consul as service discovery

Nikhil Bhatnagar
Nerd For Tech
5 min readApr 23, 2023

--

Over the years, spring has grown and advanced a lot. From supporting thymeleaf to providing concepts like service discovery, zuul, spring security etc, spring has evolved the java world greatly. The advancement of spring as a technology can be attributed to many third party companies/open source communities which developed and supported spring by building libraries like spring support for kafka, spring support for excel etc.

One such important tech is service discovery. Service discovery was an advanced concept which is widely used and was supported by netflix. You can read more about spring service discovery here. However, during recent times netflix pulled out of spring discovery development and hence the search for it’s alterntives started. On such alternative was Consul. Consul, is an open source technology developed and maintained by HashiCorp. It provides features like service discovery, service mesh, support for kubernetes etc. You can read more about Consul here and more finer details about consul will be covered in subsequent articles but this article will mainly focus on installing and using consul as service discovery.

Installing HashiCorp Consul

Jumping straight onto the installation part:-

1. Go to the official website and download the software from there according to your operating system. Mac users can use brew as well for easy download of the software and it’s usage.

2. After download:-

For windows users:-

We need to bind our pc’s ip address to the consul interface. That can be done by pasting the command in your terminal:-

consul agent -server -bootstrap-expect=1 -data-dir=consul-data -ui -bind=your_pc_ip_addr

For mac users:-

Installing via brew really make things easy and automatically adds the consul binary in your class path. However, if we install via zip file, we might need to add the path on your zshrc file. This can be done by:-

$ export PATH=”$PATH:/path/to/consul”

To check whether consul is installed properly, we can simply write consul on the terminal and it will show the list of commands that follows consul.

3. Now, to start the consul server type the following command:-

$ consul agent -dev

For windows users, the above command is sufficient to start the server. Nothing else is needed.

4. Consul also has a well-defined console, which can be viewed by going on the http://localhost:8500.

Consul Dashboard as seen on localhost

Using consul for service registry and service discovery

With our consul setup and everything in place, we need to create microservices which can we set up and registered in the consul interface. To get stated with this:-

1. We need to have a microservice in place. The boilerplate of a spring microservice can be downloaded from here. Make sure to add Consul discovery as a dependency.

2. With the downloaded zip file, unzip the file and open the project in any IDE of your type.

3. Now, to allow your service to be registered on the consul dashboard, we need to annotate our service with @EnableDiscoveryClient. So go to the main application class and just below @SpringBootApplication, write @EnableDiscoveryClient.

4. Now start the application. You will get an error saying that consul requires unique id’s. To overcome this error, go to your application.properties and give a unique name to your application using the line:-

spring.application.name: your_app_name  #here it’s named consul-demo

5. Now, re-run the application and go to localhost:8500. You’ll see our service on the console.

With our services getting registered on the consul dashboard, we need to leverage the consul service registry to make an api call to other microservices (service discovery). In order to use this:-

1. Create a new microservice with the same dependencies as that of the above application and annotate this new microservice with @EnableDiscoverClient and give a unique application name using application.properties file.

2. Now create a rest controller to inside the newly created microservice inorder to call the endpoint of other controllers.

3. New controllers can be easily created using @RestController annotations. In order to make things simpler, copy paste the code as shown below.

@RestController
public class controller {

@Autowired
RestTemplate restTemplate;

@GetMapping(value = "/serviceDiscovery")
public String discoveryCall(){
return restTemplate.exchange("http://consul-demo/hello",
HttpMethod.GET, null, new ParameterizedTypeReference<String>() {}).getBody();
}
}

Rest template is the easiest way of making an interservice api call. The above return statement makes an api call to a service which is registered as “consul-demo” and has an endpoint exposed as “/hello”.

To test this controller, just go to localhost:your_port/serviceDiscovery and you’ll get the response which will be in turn coming from /hello endpoint of consul-demo.

Note:- Using rest template here would most likely throw an exception as we need to inject it’s bean into the classpath. As such, inside our main application class, we need to give a @bean of rest template and it will work.

4. The above showed way of calling an api works but it requires you to write a little more boilerplate code, plus it requires us to write the full http based url of the service to be hit. An alternative and an advanced way to do the above exact thing is as below:-

@Autowired
private DiscoveryClient client;

@GetMapping(value = "/serviceDiscovery")
public String discoveryCall(){

URI uri= client.getInstances("name_of_service_you_want_to_call").stream().findFirst().map(si -> si.getUri())
.map(s -> s.resolve("/hello")).get();
return restTemplate.getForEntity(uri, String.class).getBody();
}

Walking through line by line,

-> Client is an object of discovery client which comes out of the box with console dependency.

-> Client object uses getInstances method on the service we want to access. This method basically access all the active clusters of the service and brings them together as a list. Make sure, that the service name is same as that registered on the consul dashboard.

-> With the list of instances, we use the first instance and get the physical port based uri of the instance.

-> After getting the instance, we resolve the endpoint we need to finally access and generate the full uri.

-> Using the rest template, we make an api call to get the response from the endpoint we want to hit.

Note:- a. Try printing the uri value we get above from the list of instances. You’ll see a little difference between the two methods mentioned in point 3 and 4.

b. Sometimes, the rest template object might throw an error saying there are no instances with the hostname mentioned in uri. In that case, try removing @LoadBalanced from rest template object or try to look through this link to get your desired solution.

This sums up the articles for using consul as service discovery. There are many ways to use consul as service discovery and this might be a very basic one but apart from service discovery, consul provides a lot many features which will be covered in subsequent articles but till then stay tuned and happy reading :).

--

--

Nikhil Bhatnagar
Nerd For Tech

A tech enthusiast who loves to read and write about new technologies and trends. Software engineer @HashedinByDeloitte