Enterprise Java

Handle the Blocking Method in Non-blocking Context Warning

With the rise of reactive programming, frameworks like Spring WebFlux provide non-blocking and asynchronous solutions for handling web requests. However, using blocking methods (such as database queries or file I/O operations) in a non-blocking context can lead to performance bottlenecks. Let us delve into understanding how handle the blocking method in a non-blocking context warning in Java.

1. Blocking Method in Non-Blocking Context in Spring Reactive

Spring WebFlux is built around the Reactor Library, enabling asynchronous processing. Blocking methods disrupt the non-blocking nature, causing thread starvation in reactive pipelines. The key to handling this issue is offloading blocking calls to a dedicated thread pool. This approach ensures that blocking operations do not occupy the limited threads of the reactive event loop, which are crucial for maintaining the scalability and responsiveness of the application. By properly managing blocking calls, developers can achieve a seamless integration of synchronous and asynchronous workflows, enhancing the overall performance and reliability of their systems.

2. Code Example

Consider the following example where a blocking database call is used in a reactive pipeline:

package com.jcg.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class BlockingController {

    @GetMapping("/blocking")
    public Mono<String> handleBlockingCall() {
        return Mono.fromSupplier(this::blockingDatabaseCall)
                   .doOnNext(data -> System.out.println("Fetched: " + data));
    }

    private String blockingDatabaseCall() {
        try {
            Thread.sleep(2000); // Simulate blocking I/O
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Blocking data";
    }
}

2.1 Code Explanation

The provided code defines a Spring WebFlux controller named BlockingController. It includes an endpoint /blocking that demonstrates a blocking operation in a reactive context.

The @RestController annotation marks this class as a RESTful web controller. The @GetMapping("/blocking") annotation maps HTTP GET requests to the handleBlockingCall method.

The method handleBlockingCall returns a Mono<String>, which is a reactive type representing a single value. Inside the method, a blocking operation is wrapped using Mono.fromSupplier, which defers the execution of the blocking method blockingDatabaseCall until subscribed. Additionally, doOnNext is used to log the fetched data for debugging purposes.

The blockingDatabaseCall method simulates a blocking I/O operation by introducing a delay of 2 seconds using Thread.sleep. If an InterruptedException occurs during sleep, the thread’s interrupted status is restored. After the delay, it returns a string value, “Blocking data.”

This implementation introduces a performance issue because the blocking call (Thread.sleep) is executed on the same thread used by the reactive pipeline. This can lead to thread starvation in high-concurrency scenarios, making the application less scalable.

2.2 Code Output

When hitting the /blocking endpoint, you will see delays due to the blocking nature:

Blocking data (after ~2 seconds)

This blocks the Netty thread, reducing scalability.

3. Fixing the Issue: Using Schedulers

To handle blocking calls effectively, offload them to a dedicated thread pool using Schedulers.boundedElastic():

package com.jcg.example;

import reactor.core.scheduler.Schedulers;

@GetMapping("/non-blocking")
public Mono<String> handleNonBlockingCall() {
    return Mono.fromSupplier(this::blockingDatabaseCall)
               .subscribeOn(Schedulers.boundedElastic())
               .doOnNext(data -> System.out.println("Fetched: " + data));
}

3.1 Code Explanation

The provided code defines a method handleNonBlockingCall within a Spring WebFlux controller to handle potentially blocking operations in a non-blocking and efficient manner. This method is mapped to the /non-blocking endpoint using the @GetMapping annotation, which processes HTTP GET requests.

The method returns a Mono<String>, a reactive type that emits a single value. The blocking operation, represented by the blockingDatabaseCall method, is wrapped using Mono.fromSupplier. This ensures that the execution of the blocking method is deferred until it is subscribed to within the reactive pipeline.

To address the blocking nature of the blockingDatabaseCall, the code uses subscribeOn(Schedulers.boundedElastic()). This instructs the framework to execute the blocking operation on a bounded elastic thread pool, which is specifically designed for tasks that involve blocking I/O or long-running operations. This approach prevents the blocking call from impacting the main reactive thread pool, ensuring better scalability and responsiveness.

Additionally, doOnNext is used to log the emitted data. When the blocking operation completes, it prints the fetched data to the console, providing a side-effect useful for debugging or monitoring.

This implementation ensures that the application’s reactive nature is preserved while handling blocking operations efficiently, making it suitable for high-concurrency scenarios without risking thread starvation.

3.2 Code Output

When hitting the /non-blocking endpoint, the main thread remains free for other tasks:

Fetched: Blocking data

4. Conclusion

Handling blocking methods in a non-blocking context is crucial for achieving high scalability in reactive applications. By offloading blocking operations to dedicated thread pools, you can maintain the asynchronous nature of the pipeline and avoid performance bottlenecks. Remember to identify potential blocking calls in your application and use techniques like Schedulers.boundedElastic() for seamless integration.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button