Skip to main content

Service Discovery

In a microservices world, services change IP addresses dynamically (scaling up/down). Hardcoding URLs (http://localhost:8081) is impossible in production. We need a “Phonebook”.

1. Netflix Eureka Server

Eureka is a Service Registry. Services register themselves here, and other services query it to find them. Setup Eureka Server
  1. Create a new Spring Boot project.
  2. Dependency: spring-cloud-starter-netflix-eureka-server.
  3. Annotation: @EnableEurekaServer.
  4. Configuration (application.yml):
server:
  port: 8761

eureka:
  client:
    register-with-eureka: false # It is the server, doesn't need to register itself
    fetch-registry: false

2. Eureka Client (The Microservice)

Now, let’s connect a User Service to Eureka.
  1. Dependency: spring-cloud-starter-netflix-eureka-client.
  2. Configuration:
spring:
  application:
    name: USER-SERVICE # Important! This is the ID in the registry

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
When you start the app, it will register with Eureka. Check http://localhost:8761 to see it listed.

3. Service-to-Service Communication

How does Order Service call User Service without knowing the IP?

The Flow: Client-Side Load Balancing

In the old days (Hardware LB), the heavy lifting was done by a central F5/NGINX. In Microservices, the Client (Order Service) is smart.

RestTemplate (Deprecated/Legacy)

@LoadBalanced // Magic annotation!
@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

// In Service
String url = "http://USER-SERVICE/users/" + userId;
UserDto user = restTemplate.getForObject(url, UserDto.class);
Spring intercepts the request, looks up USER-SERVICE in Eureka, gets a list of IPs, and uses Round Robin load balancing to pick one.

WebClient (Reactive & Modern)

The recommended way in modern Spring.
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
    return WebClient.builder();
}

// In Service
UserDto user = webClientBuilder.build()
    .get()
    .uri("http://USER-SERVICE/users/{id}", userId)
    .retrieve()
    .bodyToMono(UserDto.class)
    .block(); // Blocking for sync code

Feign Client (Declarative)

The cleanest way. It looks like a JPA Repository but for HTTP calls.
  1. Dependency: spring-cloud-starter-openfeign.
  2. Annotation: @EnableFeignClients.
@FeignClient(name = "USER-SERVICE")
public interface UserClient {

    @GetMapping("/users/{id}")
    UserDto getUser(@PathVariable("id") Long id);
}
Now just autowire UserClient and call getUser(id). Spring handles the lookup, load balancing, and HTTP serialization!