Skip to content

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:

@SpringBootApplication
@EnableEurekaServer
public class Application {

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

}

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:

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka-server</artifactId>
	</dependency>
</dependencies>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Brixton.SR5</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

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:

# Not a client, don't register with yourself
eureka.client.registerWithEureka: false
eureka.client.fetchRegistry: false

server.port=8761

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:

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka</artifactId>
	</dependency>
</dependencies>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Brixton.SR5</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

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

@Configuration
@EnableDiscoveryClient
public class RestConfig {

	// Application beans configured here
}

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:

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

spring.application.name=spanners-api

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:

@Service
public class WebAccountsService {

    @Autowired
    @LoadBalanced
    protected RestTemplate restTemplate; 

    protected String serviceUrl = "http://ACCOUNTS-SERVICE"; // ACCOUNTS-SERVICE is the name of the microservice we're calling

    public Account getByNumber(String accountNumber) {
        Account account = restTemplate.getForObject(serviceUrl
                + "/accounts/{number}", Account.class, accountNumber);

        if (account == null)
            throw new AccountNotFoundException(accountNumber);
        else
            return account;
    }
    ...
}

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:

ribbon.http.client.enabled=true

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:

@Configuration
@ConditionalOnClass(HttpRequest.class)
@ConditionalOnProperty(value = "ribbon.http.client.enabled", matchIfMissing = false)
public class RestClientConfig {

    /**
     * Customize the RestTemplate to use Ribbon load balancer to resolve service endpoints
     */
    @Bean
    public RestTemplateCustomizer ribbonClientRestTemplateCustomizer(
            final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
        return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate restTemplate) {
                restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
            }
        };
    }

}

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.

Published inSpring Boot

10 Comments

  1. Chris Chris

    Very informative article! Thank you for writing it.

    You say that “As of Spring Boot 1.4, it’s no longer recommended to directly @Autowire an instance of RestTemplate into a Rest client class.”

    Can you provide a source for this recommendation?

  2. Arpit thakkar Arpit thakkar

    Hi Stuart,
    I am new to this microservices and eureka configuration.
    Can you please help me by sharing some sample code or link from where i can get understanding reference for creating structure with spring boot and eureka without docker.
    As even i am newer to docker.
    But for now as per the requirement i need to create a structure with spring boot fusing microservices and eureka.
    Please help me.It would be really great if you give some solution.

    Thanks,
    Arpit Thakkar

  3. Sridhar Sridhar

    Thanks for your informative article.

    I have one question. When we have more than one Eureka server instance running for scalability and failover reasons, how will the client make a lookup to the Eureka via the Resttemplate.

    Since there are more than one Eureka Server instances how does the client decide which one. Does Ribbon does the load balancing for that too similar to the way it does for target service being consumed?

  4. sivabalan sivabalan

    Is there any way to hide or restrict the Eureka server status page to outside world ?

    • I would expect that you’d want to protect your Eureka server at the network / firewall level. If you’re on a traditional LAN, you’d put it on an internal (not internet facing) server. If you’re in a cloud infrastructure such as AWS, you’d put it on an internal security group.

      If you need this to be on a server exposed to the outside world, you may be able to use Spring Security to restrict access. If you’re using Spring Boot, add the spring-boot-starter-security starter and you can then use all the Spring Security features. You could restrict the status page using a simple username / password or you could restrict by other means – for example by IP address.

  5. Atul Mehetre Atul Mehetre

    I have one Question ?

    If i want to do Integration Testing of The Controller present in your Account-Service using @SpringBootTest ,then is there is need of registration of Account-Service to Discovery Server for Integration Testing ??

Leave a Reply

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