Service Discovery

How Microservices Find Each Other Without Hardcoded Addresses

The Problem

In a monolith, everything is in one place. But with microservices, you have dozens of services running on different servers, different ports, constantly scaling up and down. How does the Order Service know where the User Service is?

// BAD: Hardcoded addresses
String userServiceUrl = "http://192.168.1.100:8080/users";
// What if the IP changes? What if there are 5 instances?

// GOOD: Service discovery
String userServiceUrl = discoveryClient.getService("user-service").getUri();
// Let the system figure out where it is!

Dynamic Registration

Services register themselves when they start, deregister when they stop.

Load Balancing

Multiple instances? Discovery returns one - with built-in load balancing.

Health Checking

Automatically removes unhealthy instances from the registry.

Netflix Eureka Setup

1. Eureka Server

<!-- pom.xml for Eureka Server -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
# application.yml for Eureka Server
server:
  port: 8761

eureka:
  client:
    register-with-eureka: false  # Server doesn't register itself
    fetch-registry: false
  server:
    enable-self-preservation: false  # For development

2. Eureka Client (Your Services)

<!-- pom.xml for any microservice -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
# application.yml for User Service
spring:
  application:
    name: user-service  # This is how other services find you

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

That's it! Your service now registers with Eureka automatically.

Calling Other Services

@Service
public class OrderService {

    @Autowired
    private DiscoveryClient discoveryClient;

    // Option 1: Manual discovery
    public User getUser(Long userId) {
        List<ServiceInstance> instances =
            discoveryClient.getInstances("user-service");

        if (instances.isEmpty()) {
            throw new RuntimeException("No user-service available");
        }

        ServiceInstance instance = instances.get(0);
        String url = instance.getUri() + "/users/" + userId;

        return restTemplate.getForObject(url, User.class);
    }
}

Better: Load-Balanced RestTemplate

@Configuration
public class RestTemplateConfig {

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

@Service
public class OrderService {

    @Autowired
    private RestTemplate restTemplate;

    public User getUser(Long userId) {
        // Use service name instead of URL!
        return restTemplate.getForObject(
            "http://user-service/users/" + userId,
            User.class
        );
    }
}

Best: OpenFeign Client

// Just declare an interface - Spring handles the rest
@FeignClient(name = "user-service")
public interface UserClient {

    @GetMapping("/users/{id}")
    User getUser(@PathVariable Long id);

    @PostMapping("/users")
    User createUser(@RequestBody User user);
}

@Service
public class OrderService {

    @Autowired
    private UserClient userClient;

    public User getUser(Long userId) {
        return userClient.getUser(userId);  // So clean!
    }
}

Consul: Alternative to Eureka

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
# application.yml
spring:
  application:
    name: user-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        health-check-interval: 10s
        instance-id: ${spring.application.name}:${random.value}

Eureka vs Consul

Feature Eureka Consul
Setup Need to run Eureka Server Standalone binary
Health Checks Client-side heartbeat Active health checks
Key-Value Store No Yes (config storage)
Multi-Datacenter Limited Built-in support

High Availability

# Eureka Server 1 (peer with Server 2)
eureka:
  instance:
    hostname: eureka1
  client:
    service-url:
      defaultZone: http://eureka2:8761/eureka/

# Eureka Server 2 (peer with Server 1)
eureka:
  instance:
    hostname: eureka2
  client:
    service-url:
      defaultZone: http://eureka1:8761/eureka/

# Client connects to both
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/

Kubernetes Service Discovery

In Kubernetes, you often don't need Eureka - K8s has built-in service discovery:

# Kubernetes handles discovery via DNS
# Just use the service name:
spring:
  cloud:
    kubernetes:
      discovery:
        enabled: true

# Or simply use K8s Service DNS:
user-service.default.svc.cluster.local

Master Microservices Architecture

Learn service discovery, API gateways, and distributed systems.

Explore Full Stack Java Course