skip to content

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

level: seniorimportance: nice to knowfreq 30%

answer

  1. Reactive twin -> Flux<ServiceInstance>
  2. Never block WebFlux event-loop threads
  3. Used by Spring Cloud Gateway
  4. ReactiveCompositeDiscoveryClient merges impls
  5. Servlet app -> blocking DiscoveryClient

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.

solid answer

~40 s

ReactiveDiscoveryClient (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
java
@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

for a junior

Should just know a reactive version exists returning Flux instead of List.

for a middle

Should know it's for WebFlux and avoids blocking event-loop threads.

for a senior

Should connect it to Spring Cloud Gateway and ReactiveCompositeDiscoveryClient, and warn against .block().

for a principal

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

context