Microservice discovery with Spring Boot and Eureka

One of the standard problems with Microservices Architecture is the issue of service discovery. Once we’ve decomposed our application into more than a handful of distinct microservices, it becomes difficult for every service to know the address of every other service it depends on. Configuring dependencies from inside a microservice is impractical – it distributes configuration among all the microservices. It also violates the DRY principle – multiple microservice instances will need access to the same configuration settings. What’s more, it goes against the Dependency Injection design that’s supposed to be one of the benefits of the Microservices Architecture.

The standard solution is to delegate location of microservices to a new microservice. In keeping with the Single Responsibility Principle, this ‘discovery’ microservice is responsible for tracking the locations of all the other microservices and nothing else.

Netflix’s Eureka is an implementation of a discovery server and integration is provided by Spring Boot. Using Spring Boot, we can build a Eureka discovery server and have our microservices register with it.

The code for the following example can be downloaded from the Spanners demo app, version 4.4 on GitHub. The full stack exists as Docker images at DockerHub and can be started with this docker-compose file.

Building a Eureka server

Spring Boot’s opinionated design makes it easy to create a Eureka server just by annotating the entry point class with @EnableEurekaServer:

So long as the spring-cloud-starter-eureka-server dependency is present in the Maven / Gradle build config, this will start the application as a Eureka server. The starter dependency is part of the Spring Cloud project so you’ll want to use the latest Spring Cloud release train (currently Brixton.SR5) to manage your Maven dependency versions:

Spring’s Getting Started with Service Registration and Discovery describes how the same can be managed with Gradle.

Some basic configuration is required in the application.properties (or application.yml) file:

When this Eureka server starts, it will listen for registrations on port 8761. When our microservices start, they’ll make a call to Eureka to register themselves. Then they can query Eureka to find other registered servers. Eureka also provides a simple status console which can be viewed on http://localhost:8761.

Eureka console

This console shows that Eureka is running and that no instances are currently registered.

Registering services with Eureka

Now that we have a centralized discovery server, we need every service to register with it. This is about as easy as creating the server. First, we need to add the spring-cloud-starter-eureka dependency to Maven / Gradle. Again, use the spring-cloud release train to manage versions:

Then, enable discovery with the @EnableDiscoveryClient annotation on a @Configuration class or the @SpringBootApplication entry point class:

And finally, add a couple of configuration settings to the application.properties (or application.yml) config file. This tells the application where Eureka is and how this service should be named in Eureka:

Now, when we start the spanners-api service, we can see it registered in Eureka:

spanners-api registered with Eureka

Querying Eureka

In my application, our front end component (spanners-mvc) depends on two back end microservices (spanners-api and spanners-users). Instead of hard coding the locations of the two microservices in our front end component, we want it to ask Eureka. To do this we first follow the steps above to register the spanners-mvc component with Eureka. Just to check this has worked, we can look at the Eureka console to confirm that spanners-mvc and the two microservices are all registered:

spanners-mvc and two back end services registered with Eureka

Now, we can refer to the back end microservices by using their names rather than their server addresses. The current examples from Spring suggests something like this:

The @LoadBalanced annotated RestTemplate will resolve application names (ACCOUNTS-SERVICE) to a real server name / port by querying Eureka. The @LoadBalanced annotation tells Spring Boot to customize the RestTemplate with a ClientHttpRequestFactory that does a Eureka lookup before making the HTTP call. To make this work, you’ll need to add a new config setting to application.properties:

This just switches on the Ribbon load balancing behind the @LoadBalanced annotation.

If you’re interested, RibbonAutoConfiguration does the customization of the RestTemplate  and RibbonClientHttpRequestFactory does the Eureka lookup. This is all done for us, just by adding the @LoadBalanced annotation to a RestTemplate bean.

Eureka with Spring Boot 1.4 RestTemplateBuilder

As of Spring Boot 1.4, it’s no longer recommended to directly @Autowire an instance of RestTemplate into a Rest client class. Instead, we can use the RestTemplateBuilder to give us some more flexibility in configuring the RestTemplate. It also allows us to use the new @RestClientTest annotation to test Rest clients. More details on the advantages of the new RestTemplateBuilder in my previous post on the subject.

If we don’t @Autowire a RestTemplate bean though, we can’t use the @LoadBalanced annotation to customize the RestTemplate for Eureka lookups. At the time of writing, Spring has no built in solution for this as the current Spring Cloud release train (Brixton) was built for Spring Boot 1.3 – without RestTemplateBuilder support. If you want to use the RestTemplateBuilder with Eureka, you’ll need to customize the RestTemplate yourself. This is very straightforward:

This is pretty much a copy of the RibbonAutoConfiguration.RibbonClientConfig bean used to power the @LoadBalanced annotation except that it configures an instance of RestTemplateCustomizer. The RestTemplateBuilder automatically pulls in all configured RestTemplateCustomizers when it initializes so we don’t need to manually inject this into anything.

The conditional annotations on this configuration class create the RestTemplateCustomizer only if Ribbon (and Eureka) is enabled.

With this bean present, we can use the RestTemplateBuilder in our Rest client code and it will resolve our application names, just as if we had use the @LoadBalanced annotation.

Leave a Reply

Your email address will not be published. Required fields are marked *