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.
// 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:
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.
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.
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.