• 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-nio or Tomcat threads.

    • Repeated thread creation for each Kafka poll.

    • Latency spikes when DB slows down.

  • Reactive:

    • Logs from reactor-http or Netty event loops.

    • Stream-based processing logs (Flux, Mono chains).

    • 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.