What is ReactiveDiscoveryClient and when would you use it over the blocking DiscoveryClient?
answer
- Reactive twin -> Flux<ServiceInstance>
- Never block WebFlux event-loop threads
- Used by Spring Cloud Gateway
- ReactiveCompositeDiscoveryClient merges impls
- Servlet app -> blocking DiscoveryClient
basics
~10 sReactiveDiscoveryClient 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.
solid answer
~40 sReactiveDiscoveryClient (org.springframework.cloud.client.discovery.ReactiveDiscoveryClient) is the Project Reactor counterpart to the blocking DiscoveryClient. Its getInstances(serviceId) returns Flux<ServiceInstance> and getServices() returns Flux<String>, so lookups compose into a non-blocking pipeline and never block a WebFlux event-loop thread. You choose it in fully reactive applications, particularly Spring Cloud Gateway (which is WebFlux-based) and reactive WebClient call paths, where blocking discovery calls would starve the small event-loop pool. Backends provide reactive implementations (e.g. EurekaReactiveDiscoveryClient), and multiple are merged by ReactiveCompositeDiscoveryClient, mirroring the blocking side. In practice much registry data is served from a local cache so the 'blocking' call is cheap, but on a reactive stack you still prefer the reactive API for consistency and to avoid ever touching blocking I/O on event-loop threads. In a Servlet/MVC app the plain DiscoveryClient is the right choice.
code
java · 11 lines@Component
class ReactiveLookup {
private final ReactiveDiscoveryClient discoveryClient;
ReactiveLookup(ReactiveDiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; }
Flux<URI> instanceUris(String serviceId) {
// Stays non-blocking; composes into the WebFlux pipeline.
return discoveryClient.getInstances(serviceId)
.map(ServiceInstance::getUri);
}
}go deeper
Should just know a reactive version exists returning Flux instead of List.
Should know it's for WebFlux and avoids blocking event-loop threads.
Should connect it to Spring Cloud Gateway and ReactiveCompositeDiscoveryClient, and warn against .block().
Should reason about event-loop starvation, when bridging blocking/reactive is acceptable, and that reactivity here means non-blocking access, not live registry streaming.
## Two parallel abstractions Spring Cloud offers the same discovery contract in two flavors: - **Blocking:** `DiscoveryClient` — `List<ServiceInstance> getInstances(String)`, `List<String> getServices()`. Fits Servlet/Spring MVC apps. - **Reactive:** `ReactiveDiscoveryClient` — `Flux<ServiceInstance> getInstances(String)`, `Flux<String> getServices()`. Fits Spring WebFlux / Project Reactor apps. ## Why a reactive variant exists Reactive apps run on a **small event-loop thread pool** (Reactor Netty). The cardinal rule is **never block those threads**. A blocking discovery call — even if usually cheap because it reads a local cache — could occasionally hit I/O (a registry refresh) and stall the loop, hurting throughput for all in-flight requests. Returning a `Flux` keeps lookups inside the non-blocking pipeline, composing naturally with reactive `WebClient` calls, `flatMap`, retries, and backpressure. ## Where it's used - **Spring Cloud Gateway** is built on WebFlux and uses reactive discovery for its `DiscoveryClient` route locator and `lb://` routing. - Reactive `@LoadBalanced WebClient` paths rely on the reactive discovery/load-balancer stack. ## Implementations and composition Backends ship reactive implementations (e.g. `EurekaReactiveDiscoveryClient`, `ConsulReactiveDiscoveryClient`). When multiple are present, `ReactiveCompositeDiscoveryClient` aggregates them — exactly analogous to the blocking `CompositeDiscoveryClient`. ## Choosing between them - **WebFlux app** → `ReactiveDiscoveryClient`. - **Servlet/MVC app** → `DiscoveryClient`. Mixing is possible but you should use the variant matching your runtime so you don't bridge blocking and reactive worlds unnecessarily. ## Gotchas - Don't call `.block()` on the `Flux` from a reactive request thread just to reuse blocking logic — that reintroduces the exact hazard the reactive API avoids. - The reactive API doesn't magically make the registry itself reactive end-to-end; some implementations wrap cached data. The benefit is not blocking your event loop, not necessarily streaming live registry pushes. - Both variants share the same `ServiceInstance` type and semantics (eventual consistency, metadata, etc.).
- Why not just call .block() on the Flux and reuse the blocking code in a WebFlux app?Because blocking on an event-loop thread defeats the reactive model — it can starve the small Reactor Netty pool and collapse throughput under load. Keep the pipeline non-blocking instead.
- Which discovery variant does Spring Cloud Gateway use?The reactive one — Gateway is WebFlux-based, so it relies on ReactiveDiscoveryClient for its discovery-based route locator and lb:// load balancing.
saying these in an interview costs you the question
- Thinking reactive discovery gives live push updates rather than mainly non-blocking access to cached data
- Using ReactiveDiscoveryClient in a plain Servlet MVC app for no reason
- Calling .block() on the returned Flux inside a reactive request
- Assuming the reactive and blocking variants have different ServiceInstance semantics