DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • A Transaction-Grade Performance Blueprint for Spring Boot FinTech Microservices (Tracing, Histograms, and Kubernetes)
  • Optimizing High-Volume REST APIs Using Redis Caching and Spring Boot (With Load Testing Code)
  • Building Fault-Tolerant Spring Boot Microservices With Kafka and AWS
  • Designing Microservices Architecture With a Custom Spring Boot Starter and Auto-Configuration Framework

Trending

  • Slopsquatting: Building a Scanner That Catches AI-Hallucinated Packages Before They Reach Production
  • Building Threat Intelligence Pipelines Using Python, APIs, and Elasticsearch
  • 5 AI Security Incidents That Broke Things in Production (and What They Have in Common)
  • Why Stable RAG Answers Can Still Hide Unstable Evidence
  1. DZone
  2. Coding
  3. Frameworks
  4. Spring WebFlux: publishOn vs subscribeOn for Improving Microservices Performance

Spring WebFlux: publishOn vs subscribeOn for Improving Microservices Performance

This article explains the benefits of using PublishOn and SubscribeOn Reactor operators for improving microservices performance.

By 
Bablu Lal user avatar
Bablu Lal
·
Sep. 23, 24 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
11.8K Views

Join the DZone community and get the full member experience.

Join For Free

With the rise of microservices architecture, there has been a rapid acceleration in the modernization of legacy platforms, leveraging cloud infrastructure to deliver highly scalable, low-latency, and more responsive services.

Why Use Spring WebFlux?

Traditional blocking architectures often struggle to keep up performance, especially under high load. Being Spring Boot developers, we know that Spring WebFlux, introduced as part of Spring 5, offers a reactive, non-blocking programming model designed to address these challenges.

WebFlux leverages event-driven, non-blocking, asynchronous processing to maximize resource efficiency, making it particularly well-suited for I/O-intensive tasks such as database access, API calls, and streaming data.

About This Article

Adopting Spring WebFlux can significantly enhance the performance of the Spring Boot applications. Under normal load, Spring Boot WebFlux applications perform excellently, but for the scenarios where the source of the data is blocking, such as I/O-bound, the default (main) IO thread pool can cause contention if downstream responses are very slow and degrade the performance. In this article, I will try to cover how publishOn and subscribeOn reactor operators can come to the rescue.

Understanding publishOn and subscribeOn

(Note: I have used the Schedulers.boundedElastic() scheduler as a more scalable reactive thread pool group.  Spring Boot WebFlux provides other schedulers that can be used based on need.)

publishOn(Schedulers.boundedElastic()) and subscribeOn(Schedulers.boundedElastic()) are used by Reactor to control where certain parts of the reactive pipeline are executed, specifically on a different thread or thread pool. However, the two operators serve different purposes:

1. publishOn(Schedulers.boundedElastic())

Purpose

This switches the downstream execution to the specified scheduler, meaning that any operators that come after the publishOn will be executed on the provided scheduler (in this case, boundedElastic).

Use Case

If you want to switch the execution thread for all the operators after a certain point in your reactive chain (for example, to handle blocking I/O or expensive computations), publishOn is the operator to use.

Example

Java
 
Mono.fromSupplier(() -> expensiveBlockingOperation())
    .publishOn(Schedulers.boundedElastic()) // Switch downstream to boundedElastic threads
    .map(result -> process(result))
    .subscribe();


When To Use publishOn

  • Use when you need to run only the downstream operations on a specific scheduler, but want to keep the upstream on the default (or another) scheduler.
  • It is useful for separating the concerns of upstream and downstream processing, especially if upstream operators are non-blocking and you want to handle blocking operations later in the pipeline.

2. subscribeOn(Schedulers.boundedElastic())

Purpose

This changes the thread where the subscription (upstream) occurs. It moves the entire chain of operators (from subscription to completion) to the provided scheduler, meaning all the work (including upstream and downstream operators) will run on the specified scheduler.

Use Case

Use this if you need to run the entire chain (both upstream and downstream) on a specific thread pool or scheduler, such as for blocking I/O tasks or when you want to handle the subscription (data fetching, database calls, etc.) on a different thread.

Example

Java
 
Mono.fromSupplier(() -> expensiveBlockingOperation())
    .subscribeOn(Schedulers.boundedElastic()) // Runs the entire chain on boundedElastic threads
    .map(result -> process(result))
    .subscribe();


When To Use subscribeOn

  • Use when you want to run the entire pipeline (upstream + downstream) on the boundedElastic scheduler.
  • It's particularly useful for situations where the source of the data is blocking, such as I/O-bound operations (reading from disk, network calls, database queries, etc.), and you want to move everything off the default event loop thread (if using Netty or Reactor Netty).

Differences Between publishOn and subscribeOn 

  • publishOn affects only the downstream operations from the point where it is called. If placed in the middle of a chain, everything after the publishOn will be scheduled on the provided scheduler, but everything before it will stay on the previous scheduler.
  • subscribeOn affects the entire reactive chain, both upstream and downstream. It's often used to move blocking upstream operations (like I/O) to a non-blocking thread pool.

Choosing Between the Two

  • Use publishOn(Schedulers.boundedElastic())if:
    • You need fine-grained control over where specific parts of the reactive chain run.
    • You want to switch only the downstream operations (after a certain point) to a specific scheduler.
    • Example: You're performing non-blocking reactive operations first, and then want to handle blocking operations downstream in a different thread pool.
  • Use subscribeOn(Schedulers.boundedElastic())if:
    • You want to run the entire reactive chain (from the point of subscription onward) on a different scheduler.
    • The source operation (like a network call or database query) is blocking, and you want to move the blocking subscription and all subsequent operations to a specific scheduler like boundedElastic.
  • In short:
    • If you have blocking code upstream (like a blocking I/O source), use subscribeOn.
    • If you want to isolate downstream blocking work (e.g., after some non-blocking calls), use publishOn.

Common Use Case: Blocking I/O in Reactive Programming

If you're working with I/O-bound operations (like file reads, database queries, etc.), and you want to offload blocking operations to a bounded thread pool, here's how you might use these:

Database Call or a Blocking I/O Call

Java
 
Mono.fromCallable(() -> performBlockingDatabaseCall())
    .subscribeOn(Schedulers.boundedElastic()) // Offload blocking database call to boundedElastic
    .map(result -> process(result))            // Further processing
    .subscribe();


Here, the subscribeOn ensures that the entire pipeline, including the blocking I/O, runs on the boundedElastic scheduler.

Mixed Non-Blocking and Blocking Operations

Java
 
Mono.just("Initial value")
    .map(value -> transformNonBlocking(value)) // Non-blocking operation
    .publishOn(Schedulers.boundedElastic())    // Switch thread for blocking operation
    .flatMap(value -> Mono.fromCallable(() -> performBlockingOperation(value))) // Blocking operation
    .subscribe();


In this case, the publishOn ensures that only the downstream blocking work (i.e., the flatMap) is moved to a different scheduler, while the earlier non-blocking operations stay on the default one.

Summary

  • subscribeOn affects the entire reactive chain and is typically used when the source operation (like database access) is blocking.
  • publishOn switches the scheduler for all operations downstream from where it is called and is better when you want to run only certain parts of the chain on a different thread.
Thread pool Operator (extension) microservices Performance Spring Boot

Opinions expressed by DZone contributors are their own.

Related

  • A Transaction-Grade Performance Blueprint for Spring Boot FinTech Microservices (Tracing, Histograms, and Kubernetes)
  • Optimizing High-Volume REST APIs Using Redis Caching and Spring Boot (With Load Testing Code)
  • Building Fault-Tolerant Spring Boot Microservices With Kafka and AWS
  • Designing Microservices Architecture With a Custom Spring Boot Starter and Auto-Configuration Framework

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook