- Technology Illumination
- Posts
- Java Spring Boot - Reactive (WebFlux) vs. Blocking (Spring Web) Kafka Consumers: A Quick Dive
Java Spring Boot - Reactive (WebFlux) vs. Blocking (Spring Web) Kafka Consumers: A Quick Dive
Reactive and blocking Kafka consumers behave very differently under the hood in Spring Boot. This post walks through key differences in performance, thread usage, and system impact-using a side-by-side lens.
Exploring the subtle but important differences in how Spring Boot handles Kafka consumers using blocking vs. reactive paradigms.
Table of Contents
Overview
Spring Boot supports two modes for handling Kafka consumers:
Blocking with
spring-web(Servlet-based)Non-Blocking with
spring-webflux(Reactive Netty)
Both can receive messages and persist to a database, but the runtime behavior, performance characteristics, and JVM footprint differ significantly.
Setup Recap
You configured both consumers to read from the same Kafka topic and persist data to Postgres using JPA (blocking) and R2DBC (reactive). Docker was used to host Kafka and Postgres.
Each mode was activated via Spring profiles (blocking and nonblocking), with common business logic abstracted into a TradeService interface.
What Happens Inside the JVM
Blocking (Spring Web)
Each Kafka message is processed on a separate thread.
Threads wait for DB/network responses → Thread is blocked.
JVM thread pool is under pressure as load increases.
Memory usage and GC events rise with more threads.
Reactive (WebFlux)
Single-threaded event loop model.
Non-blocking DB (R2DBC) and async Kafka flow.
JVM runs with far fewer threads, efficient GC behavior.
Memory footprint stays low even under high load.
What to Look for in Logs
To understand the differences in behavior:
Blocking:
Logs from
http-nioor Tomcat threads.Repeated thread creation for each Kafka poll.
Latency spikes when DB slows down.
Reactive:
Logs from
reactor-httpor Netty event loops.Stream-based processing logs (
Flux,Monochains).Consistent throughput, lower delay patterns.
Search for keywords like:Thread, Kafka, poll, checkpoint, delay, timeout, block, reactor.
What to Check in Actuator
Using Spring Boot Actuator:
/actuator/metrics/jvm.threads.live
Blocking = High count, spikes under load Reactive = Stable, low thread count
/actuator/metrics/system.cpu.usage
Reactive consumes less CPU under high throughput
/actuator/metrics/logback.events
Useful to detect errors, timeouts, backpressure
/actuator/health
Confirm DB connections and Kafka availability in both modes
Key Learnings
Area | Blocking | Reactive |
|---|---|---|
Latency | Higher under load | Lower and more predictable |
Thread Usage | Many threads, heavy pool mgmt | Minimal threads, event loops |
Resource Load | Memory & CPU rise with threads | Efficient CPU/memory use |
Scalability | Needs tuning for high scale | Naturally scales with back-pressure |
Resilience | Thread starvation risk | Better under overload |
Final Thoughts
Use blocking if:
You’re working with legacy systems
Thread usage is not a concern
Simplicity matters more than scalability
Use reactive if:
You need high throughput Kafka consumers
You're running in containerized/cloud environments
Resource efficiency and scale are key
Reactive might look intimidating at first—but once you go through the logs, JVM metrics, and actuator dashboards, you’ll see the difference is more than just syntax. It’s a mindset shift in building performant, scalable services.