WebClient: The Reactive HTTP Client
OpenFeign is declarative and simple; WebClient is lower-level and non-blocking. This module is when the second one is actually the right tool.
Learning objectives
- Beginner: Make a simple GET request with WebClient and extract the response body.
- Intermediate: Explain the difference between a blocking call and a non-blocking, reactive one.
- Advanced: Choose between OpenFeign and WebClient for a given service-to-service integration, based on concurrency requirements.
WebClient client = WebClient.builder() .baseUrl("http://inventory-service") .build(); Mono<StockLevel> stock = client.get() .uri("/api/stock/{sku}", "SKU-123") .retrieve() .bodyToMono(StockLevel.class);
Notice the return type: Mono<StockLevel>, not StockLevel directly. This is the core difference from OpenFeign's synchronous interface — WebClient returns a reactive publisher representing a value that WILL arrive, not one that's already there.
💻 Code example
WebClient client = WebClient.builder() .baseUrl("http://inventory-service") .build(); Mono<StockLevel> stock = client.get() .uri("/api/stock/{sku}", "SKU-123") .retrieve() .bodyToMono(StockLevel.class);
A blocking HTTP call (like a plain Feign call) ties up the calling THREAD for the entire duration of the network round-trip — that thread can do nothing else until a response arrives. A non-blocking call frees the thread immediately, registering a callback to run once the response actually arrives, so the same thread can serve other work in the meantime.
| Blocking (Feign/RestTemplate) | Non-blocking (WebClient) | |
|---|---|---|
| Thread during the call | Held, idle, waiting | Released immediately |
| Threads needed for high concurrency | One per in-flight call | A small fixed pool regardless of concurrency |
| Code readability | Simple, sequential | Requires reactive-chain thinking (Mono/Flux) |
◆ Under the hood
For a typical service making a handful of downstream calls per request, blocking Feign clients are simpler to read and perfectly adequate — the thread-per-call cost is invisible at low-to-moderate concurrency. WebClient's non-blocking model earns its complexity specifically at HIGH concurrency (thousands of simultaneous in-flight calls), where a thread-per-call model would need thousands of threads just to stay idle waiting on network I/O, while WebClient serves the same load with a handful.
✓ Quick recap
- WebClient returns Mono/Flux (a publisher for a future value), not the value directly — this is the reactive programming model.
- Blocking calls hold a thread idle for the whole round-trip; non-blocking calls release the thread immediately and resume via callback.
- Choose WebClient when concurrency is genuinely high enough that thread-per-call would be a real bottleneck — otherwise, OpenFeign's simplicity usually wins.
Want a visual for this concept?
Generate a diagram tailored to “WebClient: The Reactive HTTP Client” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →