Open In App

Backpressure in Spring WebFlux

Last Updated : 04 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Backpressure is implemented in Spring WebFlux using the Flux and Mono API. A publisher using Flux or Mono does not send data to its subscribers when it generates it. Problems like resource contention, delay, and possible system failure might result from this imbalance.

Backpressure is a system that allows the consumer to tell the producer to slow down, which helps us deal with this imbalance. In this article, we will learn about Backpressure in Spring WebFlux.

Example of Backpressure in Spring WebFlux

Let’s go through a simple example to understand the backpressure concept.

Java




// Generate a Flux of integers from 1 to 100
Flux.range(1, 100)
    // Log events (e.g., onNext, onComplete, onError)
    .log()
    // Subscribe to the Flux with a custom BaseSubscriber
    .subscribe(new BaseSubscriber<Integer>() {
        // Hook executed when the subscription is established
        @Override
        protected void hookOnSubscribe(Subscription subscription) {
            // Request the first 10 elements upon subscription
            request(10);
        }
  
        // Hook executed when a new element is received
        @Override
        protected void hookOnNext(Integer value) {
            // Process the received value
            System.out.println(value);
              
            // Request the next 10 elements when a multiple of 10 is encountered
            if (value % 10 == 0) {
                request(10);
            }
        }
    });


Control Backpressure:

  • Only send out new events in response to subscriber requests. To gather elements at the emitter’s request, use this pull method. Putting a cap on how many events the client-side can get.
  • When using a limited push strategy, the publisher is only able to transmit a certain number of things to the customer at once.
  • When the user is unable to process any more events, the data streaming will be stopped. The receiver in this scenario has the option to stop the broadcast at any moment and rejoin the stream at a later time.

Step-by-Step Implementation of Backpressure in Spring WebFlux

Below are the steps to implement Backpressure in Spring WebFlux.

Step 1: Add Spring WebFlux starter and Reactor test dependencies

We only need to add the Reactor test dependencies and the Spring WebFlux starter to our pom.xml file in order to execute the examples:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>

Step 2: Requests new events

Putting the consumer in charge of what events it can process is the first choice. The publisher therefore has to wait till the recipient requests fresh events. In conclusion, after subscribing to the Flux, the client processes the events in accordance with its demand:

Java




import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
  
public class FluxChunkTest {
  
    @Test
    public void whenRequestingChunks10_thenMessagesAreReceived() {
        // Create a Flux of integers from 15 to 35
        Flux<Integer> fluxChunkTest = Flux.range(15, 20);
  
        // Use StepVerifier to assert and verify the behavior of the Flux
        StepVerifier.create(fluxChunkTest)
                // Expect a subscription to be initiated
                .expectSubscription()
                // Request the first 10 elements and expect them
                .thenRequest(10)
                .expectNext(15, 16, 17, 18, 19, 20, 21, 22, 23, 24)
                // Repeat the process for subsequent chunks of 10 elements
                .thenRequest(10)
                .expectNext(25, 26, 27, 28, 29, 30, 31, 32, 33, 34)
                .thenRequest(10)
                .expectNext(35)
                // Verify that the Flux has completed successfully
                .verifyComplete();
    }
}


  • The emitter never overpowers the receiver while using this method.
  • Put differently, the client is in charge of processing the events that are required of it.
  • Using StepVerifier, we will test the producer’s response to backpressure.
  • Only when thenRequest(n) is called can we anticipate the following n elements.

Step 3: Use the limitRange() operator

We can utilize Project Reactor’s limitRange() operator. Set how many items to prefetch at once is possible with it.

Java




import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
  
public class LimitRateTest {
  
    @Test
    public void whenLimitRateSet_thenSplitIntoChunks() throws InterruptedException {
        // Create a Flux of integers from 1 to 15
        Flux<Integer> flux = Flux.range(1, 15);
  
        // Limit the rate at which elements are emitted to 5 per request
        Flux<Integer> limit = flux.limitRate(5);
  
        // Use StepVerifier to assert and verify the behavior of the Flux
        StepVerifier.create(limit)
                // Expect a subscription to be initiated
                .expectSubscription()
                // Request the first 5 elements and expect them
                .thenRequest(5)
                .expectNext(1, 2, 3, 4, 5)
                // Request the next 5 elements and expect them
                .thenRequest(5)
                .expectNext(6, 7, 8, 9, 10)
                // Request the last 5 elements and expect them
                .thenRequest(5)
                .expectNext(11, 12, 13, 14, 15)
                // Verify that the Flux has completed successfully
                .verifyComplete();
    }
}


  • The limit remains in effect even if the subscriber asks additional events to be processed, which is an intriguing feature.
  • To prevent using more than the allotted amount for each request, the emitter divides the events into smaller chunks.

Step 4: Cancel the events

Lastly, the customer has the option to cancel the events at any time. We will use a different technique for this case. We can expand the BaseSubscriber or create our own Subscriber with Project Reactor.

Java




import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.BaseSubscriber;
import reactor.test.StepVerifier;
  
public class CancelTest {
  
    @Test
    public void whenCancel_thenSubscriptionFinished() {
        // Create a Flux of integers from 1 to 5
        Flux<Integer> flux = Flux.range(1, 5);
  
        // Subscribe to the Flux with a custom BaseSubscriber
        flux.subscribe(new BaseSubscriber<Integer>() {
            // Hook executed when a new element is received
            @Override
            protected void hookOnNext(Integer value) {
                // Request the next 2 elements
                request(2);
                // Print the received value
                System.out.println(value);
                // Cancel the subscription after processing one element
                cancel();
            }
        });
  
        // Use StepVerifier to assert and verify the behavior of the Flux
        StepVerifier.create(flux)
                // Expect the first 2 elements to be received
                .expectNext(1, 2)
                // Cancel the subscription and verify completion
                .thenCancel()
                .verify();
    }
}


This is Backpressure in Spring WebFlux. Backpressure is created WebFlux using the Flux and Mono API.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads