original
https://foojay.io/today/backpressure-in-reactive-systems/?spm=ata.21736010.0.0.16518248eDx9MZ
In mid-January, I made a share based on my article The prerequisites for migrating to Reactive Spring Boot applications
https://www.youtube.com/watch?v=w0b4OQQmhBI
Since it was a Kotlin meetup, I showed it in Kotlin code, and I added a step to migrate the codebase to coroutines.
During the QA session, someone asked if the coroutine implemented backpressure. I admit I wasn't sure either, so I did a little research.
This article provides general information about backpressure and how to handle it with Rxjava (v3), Project Reactor and Kotlin's Coroutines.
What is back pressure?
Back pressure is the resisting or opposing force on the fluid in the pipeline, resulting in a loss of friction and a reduction in pressure. The term back pressure is inappropriate, pressure is a scalar quantity with magnitude but no direction - Wikipedia
In software, backpressure is somewhat related to this but has a different meaning: Assuming there is a fast sender of data and a slower receiver of data, backpressure is a mechanism that pushes the sender in the opposite direction not to send the receiver back. Fang crushed.
Whether reactivestreams.org or java.until.concurrent.Flow, reactive streams provide the following four building blocks
- Publisher sends element
- Subscriber reacts to received elements
- A Subscription to bind Publisher and Subscriber
- a Processor
Here is the class diagram:
The request() method of Subscription is the top layer of backpressure.
The specification is straightforward:
Subscriber must receive the onNext signal after sending the demand signal through Subscription.request(long n). The implicit rule here is that the Subscriber decides when and how many elements to receive. To avoid signal reordering caused by reentrant Subscription methods, it is strongly recommended that Subscriber method implementations use a synchronous manner for any signal handling at the end of the Subscription method call. It is recommended that Subscribers request the upper limit of what they can handle, as requesting only one element at a time would result in an inefficient "stop and wait" protocol. -- JVM's Reactive Stream Specification
The specification of the response stream is pretty standard. They also have Java-based TCKs.
But it is beyond the scope of this specification to define how to manage producers to send elements that cannot be processed downstream. The problem is relatively simple, and there are many solutions. Each Reactive framework has a solution, let's take a look.
Backpressure for RxJava 3
RxJava v3 provides the following base classes:
kind | describe |
---|---|
Flowable | A stream of elements 0 to N. Supports Reactive-flow and backpressure |
Observable | A stream of 0 to N elements. Backpressure is not supported |
Single | an exact stream: 1 element or an error |
Maybe | A stream that includes: no element an element or an error |
Completable | A stream has no elements but: is a completion end or an error signal |
Of these classes, Flowable is the only one that implements Reactive Flow - Backpressure. Therefore, providing back pressure is not the only problem. The RxJava wiki states:
Backpressure does not address overproduction of Observables or overconsumption of Subscribers. It just moves the problem out of the deal chain to a better place to deal with it. --Responsive backpressure is not a panacea.
To address this, RxJava provides two main strategies for dealing with "overproduced" elements:
- Store the element in a cache, if there is not enough cache, an OutOfMemoryError may be raised.
- lose data
The following diagram depicts different implementations of these strategies:
Remember that the onBackPressureLatest operation is similar to using onBackpressureBuffer(1):
This image is from the RxJava Wiki.
Unlike other frameworks, RxJava provides methods to signal an overflow exception after all elements have been sent. This allows the consumer to receive the data while being aware that the sender has lost the data.
Backpressure in Project Reactor
The strategies provided in Project Reactor are similar to RxJava.
The API is a bit different. For example, if the producer overflows Project Reactor provides a convenience method to throw an exception:
var stream = Stream.generate(Math::random);
// RxJava
Flowable.fromStream(stream) // 1
.onBackpressureBuffer(0); // 2// Project Reactor
Flux.fromStream(stream) // 1
.onBackpressureError(); // 2
- Create Reactive Streams
- Throws an exception if the producer overflows
Here is the Flux class diagram with the backpressure capability highlighted:
Compared to other frameworks, Project Reactor provides a way to set cache TTL to prevent overflow.
Backpressure in coroutines
Coroutines provide the same caching and invalidation capabilities. The base class for coroutines is Flow.
You can use like this:
flow { // 1
while (true) emit(Math.random()) // 2
}.buffer(10)
- Create a Flow class and define content by the following
- Define the content of the Flow
- Set the cache size to 10
in conclusion
RxJava, Project Reactor, and Kotlin coroutines all provide backpressure capabilities. Provides two strategies when the producer is faster than the consumer: cache data or discard data.
More:
Reactive Streams JVM specifications
https://github.com/reactive-streams/reactive-streams-jvm
How (not) to use Reactive Streams in Java 9+
https://blog.softwaremill.com/how-not-to-use-reactive-streams-in-java-9-7a39ea9c2cb3
RxJava Backpressure
https://github.com/ReactiveX/RxJava/blob/3.x/docs/Backpressure.md
This article is from Zhu Kunrong's WeChat public account "Malt Bread", the public account id "darkjune\_think"
Developer/Sci-Fi Enthusiast/Hardcore Console Gamer/Amateur Translator
Please indicate when reprinting.
Station B: https://space.bilibili.com/23185593/
Communication Email: zhukunrong@yeah.net
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。