skip to content

Service Discovery & Load Balancing

How services find each other: Eureka, Consul, the DiscoveryClient abstraction, client-side load balancing, and Kubernetes-native discovery. Interviewers ask because 'how does service A find service B' is the first question in any microservice design.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

25

What is Spring Cloud's DiscoveryClient and what problem does it solve? What does @EnableDiscoveryClient do?

level: juniorimportance: must knowfreq 70%

answer

  1. Look up instances by logical name, not IP
  2. getInstances(serviceId) -> ServiceInstance list
  3. @EnableDiscoveryClient = marker, now often optional
  4. Registry = Eureka/Consul/Zookeeper
  5. Lookup only, not load-balancing

basics

~20 s

DiscoveryClient is a Spring interface to look up running instances of a service by its logical name instead of hardcoding host/port. @EnableDiscoveryClient signals that the app registers with and queries a service registry (Eureka, Consul, etc.).

solid answer

~40 s

In microservices, instances start, stop, and move, so their IP/port are not stable. A service registry (Eureka, Consul, Zookeeper) tracks which instances are up under a logical service id. Spring Cloud's DiscoveryClient is the client-side abstraction over that registry: you call getInstances("order-service") and get back live ServiceInstance objects (host, port, metadata) without knowing the concrete addresses. @EnableDiscoveryClient historically activated the discovery integration; with modern starters (spring-cloud-starter-netflix-eureka-client, etc.) auto-configuration usually enables it just by having the starter on the classpath, so the annotation is largely optional today but still commonly seen and used to be explicit. The point is decoupling: callers depend on a name, not an address, so instances can scale and relocate freely.

code

java · 19 lines
java
@SpringBootApplication
@EnableDiscoveryClient // optional with modern starters, but explicit
public class GatewayApp {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApp.class, args);
    }
}

@Service
class OrderLookup {
    private final DiscoveryClient discoveryClient;
    OrderLookup(DiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; }

    void printInstances() {
        for (ServiceInstance si : discoveryClient.getInstances("order-service")) {
            System.out.println(si.getUri() + " meta=" + si.getMetadata());
        }
    }
}

go deeper

for a junior

Should explain the 'don't hardcode host/port' idea and that a registry tracks live instances.

for a middle

Should know the ServiceInstance shape and that the annotation is now often optional.

for a senior

Should separate lookup (DiscoveryClient) from load balancing/HTTP and mention eventual consistency of the cache.

for a principal

Should discuss autoRegister semantics, platform-native discovery (k8s DNS) as an alternative, and cache-staleness trade-offs.

## The problem In a microservice system, service A needs to call service B over HTTP. Hardcoding `http://192.168.1.5:8080` breaks the moment B scales out, restarts on a new port, or moves to a new host (common with containers/Kubernetes/cloud autoscaling). **Service discovery** solves this by introducing a **service registry** — a directory that every service instance registers itself into on startup and deregisters from on shutdown, keyed by a **logical service id** (e.g. `order-service`). ## What DiscoveryClient is `org.springframework.cloud.client.discovery.DiscoveryClient` is a Spring Cloud **interface** (an SPI — Service Provider Interface) that gives your code a **vendor-neutral, read-side view** of the registry. Its core methods: - `List<String> getServices()` — all known logical service ids. - `List<ServiceInstance> getInstances(String serviceId)` — the currently-known live instances of one service. Each `ServiceInstance` exposes `getServiceId()`, `getHost()`, `getPort()`, `isSecure()`, `getUri()`, and `getMetadata()` (a `Map<String,String>` of registration metadata such as zone, version, weight). ## @EnableDiscoveryClient `@EnableDiscoveryClient` (in `org.springframework.cloud.client.discovery`) is a marker annotation that turns on the discovery-client integration for whatever discovery implementation is on the classpath. **Important nuance:** with current Spring Cloud releases, simply adding a discovery starter (e.g. `spring-cloud-starter-netflix-eureka-client`, `-consul-discovery`, `-zookeeper-discovery`) auto-configures discovery, so the annotation is **optional** — the app registers and can query the registry without it. The annotation has an `autoRegister` attribute (default `true`); setting it `false` lets an app **consume** discovery (query other services) without **registering itself**. You'll still see `@EnableDiscoveryClient` widely in codebases and tutorials as an explicit signal of intent. ## How a call flows 1. On startup each B instance registers `{serviceId: order-service, host, port, metadata}` with the registry. 2. Service A (a discovery client) periodically fetches/refreshes the registry into a **local cache**. 3. A calls `discoveryClient.getInstances("order-service")`, picks one instance (often via a load balancer), and makes the HTTP call to that host:port. ## When to use it Use it whenever you have dynamic, horizontally-scaled services behind logical names and you're **not** relying purely on platform DNS/service-mesh discovery. In Kubernetes many teams use k8s Services (DNS) instead, but Spring Cloud Kubernetes also provides a DiscoveryClient implementation backed by the k8s API. ## Gotchas - DiscoveryClient is only the **lookup** side; it does **not** load-balance or make HTTP calls — that's Spring Cloud LoadBalancer / a `@LoadBalanced` RestClient/WebClient. - Data is **eventually consistent**: the local cache can lag reality, so `getInstances` may briefly return a dead instance or omit a fresh one.

  • Does @EnableDiscoveryClient make an HTTP call to another service for you?
    No. It only wires up registry integration so you can register/look up instances. Actually calling a service requires a (usually load-balanced) HTTP client like RestClient/WebClient or an OpenFeign client.
  • Is the annotation still required in recent Spring Cloud?
    Generally no — the discovery starter's auto-configuration enables it. It's kept for clarity and for the autoRegister=false use case (consume without registering).

saying these in an interview costs you the question

  • Claiming DiscoveryClient performs the load balancing and HTTP call itself
  • Saying the annotation is strictly mandatory in all versions
  • Thinking getInstances returns addresses you configured manually rather than live registrations

context

open as a page

How does a Spring Boot service register itself with HashiCorp Consul for service discovery, and what dependency/config is required?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Add the spring-cloud-starter-consul-discovery dependency. On startup the app auto-registers with a local Consul agent using its service name and port, so other services can look it up by name.

open as a page

What is Netflix Eureka, and what do @EnableEurekaServer and @EnableEurekaClient do?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Eureka is a service registry: services register themselves so others can find them by name instead of a hardcoded address. @EnableEurekaServer starts the registry server; @EnableEurekaClient makes an app register with and query that registry.

open as a page

What is Kubernetes-native service discovery in Spring Cloud, and how does it differ from using Eureka?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Spring Cloud Kubernetes provides a DiscoveryClient that looks up other services by reading Kubernetes' own Service and Endpoints records from the API server. Unlike Eureka, there is no separate registry to run and apps do not self-register.

open as a page

What is Spring Cloud LoadBalancer and what does the @LoadBalanced annotation do?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Spring Cloud LoadBalancer is a client-side load balancer that replaced Netflix Ribbon. @LoadBalanced marks a RestTemplate or WebClient.Builder so URLs using a logical service name resolve to a real instance, spreading calls across instances.

open as a page

Explain instance registration, lease renewal heartbeats, and how the server evicts dead instances in Eureka.

level: middleimportance: must knowfreq 65%

basics

~20 s

On startup a client registers an instance and gets a lease. It then sends a heartbeat (renews the lease) every 30 seconds. If the server gets no heartbeat within the lease-expiration window (90s), the lease expires and an eviction task removes that instance from the registry.

open as a page

How do you use getInstances(serviceId) and the ServiceInstance it returns? What information does a ServiceInstance carry?

level: middleimportance: should knowfreq 55%

basics

~10 s

Inject DiscoveryClient and call getInstances("my-service") to get a List<ServiceInstance>. Each ServiceInstance gives host, port, uri, isSecure, and a metadata map. You then pick one instance and build a URL to call it.

open as a page

How do Consul health checks work with a Spring Boot app, and what happens to an instance that becomes unhealthy?

level: middleimportance: should knowfreq 55%

basics

~10 s

By default Spring registers an HTTP check hitting the app's Actuator health endpoint. The agent polls it periodically; if it fails, Consul marks the instance unhealthy and discovery stops routing traffic to it.

open as a page

How is the Consul KV store used as a configuration backend for Spring Boot, and how do property keys map into the environment?

level: middleimportance: should knowfreq 50%

basics

~10 s

Add spring-cloud-starter-consul-config and import consul: config. Spring reads keys under a prefix like config/<app-name>/ from Consul's KV store and exposes them as normal Spring properties, with app-specific keys overriding shared ones.

open as a page

How does the client-side registry cache work in Eureka, and what staleness does it introduce?

level: middleimportance: should knowfreq 45%

basics

~20 s

Each Eureka client keeps a local copy of the registry and refreshes it periodically (every 30s by default), usually by fetching only the changes (deltas). Lookups hit this local cache, not the server, so discovery is fast but can be up to ~30s stale.

open as a page

How do you enable Spring Cloud Kubernetes discovery, and what is the difference between the Fabric8 and official-client starters?

level: middleimportance: should knowfreq 45%

basics

~10 s

Add one starter — spring-cloud-starter-kubernetes-fabric8 or spring-cloud-starter-kubernetes-client — which auto-configures a DiscoveryClient. Both talk to the same Kubernetes API; they just use different underlying Java clients (Fabric8's io.fabric8 client vs the official kubernetes-client/java).

open as a page

How does @LoadBalanced actually work under the hood for RestTemplate versus WebClient?

level: middleimportance: should knowfreq 42%

basics

~20 s

For RestTemplate, @LoadBalanced adds a LoadBalancerInterceptor to the interceptor chain. For WebClient, it adds a load-balancing ExchangeFilterFunction to the builder. Both intercept the request, resolve the service id to an instance, and rewrite the URL.

open as a page

What is the default load-balancing algorithm in Spring Cloud LoadBalancer, and how do RoundRobin and Random differ and get configured?

level: middleimportance: should knowfreq 38%

basics

~20 s

The default is RoundRobinLoadBalancer, which cycles instances in order using an atomic counter. RandomLoadBalancer picks a random instance each time. You switch by defining a ReactorLoadBalancer bean in a per-client configuration class referenced via @LoadBalancerClient.

open as a page

How does the DiscoveryClient SPI abstract different registries (Eureka, Consul, Zookeeper), and how does Spring Cloud combine multiple discovery sources?

level: seniorimportance: should knowfreq 45%

basics

~20 s

DiscoveryClient is a common interface; each backend (Eureka, Consul, Zookeeper) ships its own implementation via a starter. Your code depends only on the interface, so switching registries means swapping the starter. Spring Cloud can also merge several implementations behind one CompositeDiscoveryClient.

open as a page

Explain how Consul keeps its service catalog and KV store consistent, and how it tracks cluster membership — i.e. Raft vs gossip.

level: seniorimportance: should knowfreq 40%

basics

~20 s

Consul servers use the Raft consensus algorithm to keep one consistent, replicated copy of the catalog and KV store (writes go through an elected leader). Membership and failure detection use a gossip protocol (Serf/SWIM) across all agents.

open as a page

What is Eureka's self-preservation mode, why does it exist, and what problems can it cause?

level: seniorimportance: should knowfreq 50%

basics

~20 s

If the server suddenly stops receiving enough heartbeats (below ~85% of expected), it assumes a network problem rather than mass instance death and stops evicting instances — keeping them registered to avoid wiping out a healthy registry. The downside: it can keep dead instances listed during a real outage.

open as a page

How does Spring Cloud Kubernetes turn a Service into a list of ServiceInstances, and how do readiness, ports, and metadata factor in?

level: seniorimportance: should knowfreq 40%

basics

~20 s

For a serviceId it finds the matching Kubernetes Service and reads its Endpoints. Each ready pod address in the Endpoints subsets becomes one ServiceInstance, with pod IP, chosen port, and metadata built from the Service's labels, annotations, and ports.

open as a page

What RBAC and security setup does Spring Cloud Kubernetes discovery require, and how do you minimize the permission blast radius?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The pod's ServiceAccount needs read access (get/list/watch) to Kubernetes services and endpoints in the relevant namespaces. Grant it with a Role/RoleBinding (namespace-scoped) — or a ClusterRole for all-namespaces. To avoid giving every app this access, route through a Discovery Server that holds the permissions.

open as a page

What is ServiceInstanceListSupplier and how does caching work in Spring Cloud LoadBalancer?

level: seniorimportance: should knowfreq 30%

basics

~20 s

ServiceInstanceListSupplier provides the current list of instances for a service to the load balancer. By default SCLB wraps a discovery-backed supplier with CachingServiceInstanceListSupplier so it doesn't hit the discovery client on every request; cache TTL defaults to 35s.

open as a page

Where does DiscoveryClient sit relative to Spring Cloud LoadBalancer and @LoadBalanced clients, and what caching/staleness concerns follow?

level: principalimportance: should knowfreq 40%

basics

~20 s

DiscoveryClient only finds instances of a service. Spring Cloud LoadBalancer then picks one instance per call, and a @LoadBalanced RestClient/WebClient/Feign actually makes the HTTP request to it. Because discovery data is cached, picks can briefly target stale instances.

open as a page

What is ReactiveDiscoveryClient and when would you use it over the blocking DiscoveryClient?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

ReactiveDiscoveryClient is the non-blocking version of DiscoveryClient: getInstances returns a Flux<ServiceInstance> instead of a List. Use it in reactive/WebFlux apps so registry lookups don't block the event-loop threads.

open as a page

When would you choose Consul over Eureka for service discovery in a Spring Cloud system, and what architectural trade-offs are you accepting?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Choose Consul when you also want a consistent KV config store, health checking, multi-datacenter, and language-agnostic discovery in one tool. The trade-off is a CP system that can reject writes during partitions, versus Eureka's AP always-writable design.

open as a page

Where does Eureka sit on the CAP spectrum, and how do its design choices (peer replication, caching, self-preservation) reflect that? When might you not use it?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Eureka is an AP system: during a network partition it stays available and returns possibly-stale data rather than blocking to guarantee consistency. Everything about it — peer replication without consensus, client caches, self-preservation — favors availability over a perfectly consistent registry.

open as a page

How does the informer/watch-based discovery client keep its instance data fresh, and what are the scaling and staleness tradeoffs versus native Kubernetes Service DNS?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Newer versions use Kubernetes informers: a watch stream feeds a local in-memory cache of Services/Endpoints, so getInstances() is a fast cache read that updates as the cluster changes. It trades some staleness and apiserver watch load for client-side load balancing that plain Service DNS can't do.

open as a page

Explain the ReactorLoadBalancer / ReactiveLoadBalancer.Factory abstraction and how per-service configuration isolation works in Spring Cloud LoadBalancer.

level: principalimportance: nice to knowfreq 18%

basics

~10 s

ReactorLoadBalancer<ServiceInstance> is the interface whose choose() picks an instance. LoadBalancerClientFactory (a ReactiveLoadBalancer.Factory) builds a separate child ApplicationContext per service, so each service can have its own load balancer, supplier, and config without affecting others.

open as a page