How do you use getInstances(serviceId) and the ServiceInstance it returns? What information does a ServiceInstance carry?
answer
- getInstances -> List<ServiceInstance>
- host/port/uri/isSecure/metadata
- metadata map -> canary/zone routing
- empty list = transient, retry
- snapshot of cache, eventually consistent
basics
~10 sInject 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.
solid answer
~40 sYou inject the DiscoveryClient bean and call getInstances(serviceId), which returns a List<ServiceInstance> of the instances currently in the client's registry cache. Each ServiceInstance exposes getServiceId, getHost, getPort, isSecure, getUri (a ready-made scheme://host:port), and getMetadata (a String->String map holding registration metadata like zone, version, or weight). To call the service you'd normally hand the serviceId to a load-balanced client rather than iterate manually, but raw getInstances is useful for diagnostics, custom routing, blue-green/canary logic keyed on metadata, or health dashboards. Key caveats: the list is a snapshot of a locally-cached, eventually-consistent view, so it can contain a just-died instance or miss a just-started one; and an empty list means 'none known right now', which you must handle rather than assume the service is permanently gone.
code
java · 13 lines@Component
class CanaryRouter {
private final DiscoveryClient discoveryClient;
CanaryRouter(DiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; }
// Pick an instance whose metadata marks it as the canary version.
Optional<URI> canaryUri(String serviceId) {
return discoveryClient.getInstances(serviceId).stream()
.filter(si -> "canary".equals(si.getMetadata().get("track")))
.map(ServiceInstance::getUri)
.findFirst();
}
}go deeper
Should name getInstances and the basic ServiceInstance fields (host, port, uri).
Should know the metadata map and that the list is an eventually-consistent snapshot.
Should explain when to prefer LoadBalancer over manual iteration and how to handle empty/stale results.
Should reason about metadata-driven routing (canary/zone) and health-filtering differences across registry backends.
## The API `DiscoveryClient` (interface `org.springframework.cloud.client.discovery.DiscoveryClient`) is a bean you inject. The main call is: ```java List<ServiceInstance> getInstances(String serviceId) ``` The `serviceId` is the **logical name** a service registered under (e.g. `order-service`) — case-insensitive in most registries, and by default derived from `spring.application.name`. ## ServiceInstance `org.springframework.cloud.client.ServiceInstance` describes one running instance: - `String getServiceId()` — the logical name. - `String getHost()` / `int getPort()` — network location. - `boolean isSecure()` — https vs http. - `URI getUri()` — convenience `scheme://host:port` built from the above. - `Map<String,String> getMetadata()` — arbitrary registration metadata. Registries let each instance publish key/value pairs (zone, region, version, weight, git-commit). This is the hook for **metadata-driven routing** (canary, zone affinity). - `String getInstanceId()` (default method) — a unique instance identifier when available. ## Typical usage For real traffic you almost never iterate `getInstances` and pick manually — you let **Spring Cloud LoadBalancer** do it via a `@LoadBalanced` `RestClient`/`WebClient`/`RestTemplate`, or via an OpenFeign client, all of which take a `http://order-service/...` URL and resolve+balance under the hood using the same discovery data. Direct `getInstances` shines for: - diagnostics / an admin 'what's registered' view, - custom routing logic (choose by metadata), - warming caches or health-checking, - tests. ## Edge cases and gotchas - **Eventual consistency:** the returned list comes from a **local cache** refreshed periodically from the registry, so it lags reality. Expect occasional dead instances (until the next heartbeat expiry) and delayed appearance of new ones. - **Empty list:** means 'no known instances now', which can be transient (registry not yet fetched, all instances mid-restart). Treat it as retryable, not a hard 404. - **Ordering not guaranteed:** don't assume the list order is stable or reflects load-balancing preference — that's the load balancer's concern. - **Health filtering varies by backend:** whether down/unhealthy instances are excluded depends on the underlying registry and its config (e.g. Eureka's instance status, Consul health checks). - **Snapshot semantics:** the list is a point-in-time copy; re-call for fresh data rather than caching it yourself.
- If getInstances returns an empty list, does that mean the service is down?Not necessarily. It means no instances are known in the local cache right now — could be a not-yet-fetched registry, all instances restarting, or a registry hiccup. Treat it as retryable rather than a definitive 'gone'.
- Why usually let LoadBalancer resolve serviceId instead of calling getInstances yourself?LoadBalancer handles instance selection, health/zone awareness, retries, and caching consistently. Manual getInstances loops duplicate that and are easy to get wrong; reserve them for diagnostics or metadata-based custom routing.
saying these in an interview costs you the question
- Assuming the returned list is real-time accurate rather than a cached snapshot
- Treating an empty list as a permanent failure
- Believing list order encodes load-balancing priority
- Storing the list and reusing it indefinitely instead of re-querying