Selft training repo
Is a set of interfaces and rules that define a standard for asynchronous stream processing with non-blocking backpressure in Java. It was developed to address challenges in handling asynchronous and potentially unbounded streams of data, ensuring that data producers (publishers) don’t overwhelm data consumers (subscribers) and lead to resource exhaustion.
Represents a source of data that emits items to subscribers. Publishers emit items one by one in response to subscriber requests.
import org.reactivestreams.Subscriber;
public interface Publisher<T> {
void subscribe(Subscriber<? super T> subscriber);
}
Publishers emit three types of signals: onNext
, onError
, and onComplete
:
onNext
: represents a regular data itemonError
: indicates an error conditiononComplete
: signals the end of the streamRepresents a consumer of data emitted by a publisher. Subscribers signal demand for items (or signals) and receive them asynchronously.
import org.reactivestreams.Subscription;
public interface Subscriber<T> {
void onSubscribe(Subscription subscription);
void onNext(T item);
void onError(Throwable throwable);
void onComplete();
}
A Subscriber
requests a certain number of items from the Publisher (request(n)
) and processes them as they arrive.
Subscribers provide methods for handling each type of signal:
onNext
for processing data itemsonError
for handling errorsonComplete
for handling the completion of the streamRepresents the connection between a subscriber and a publisher. It allows subscribers to request items and cancel their subscription.
public interface Subscription {
void request(long n);
void cancel();
}
A Subscription has several important responsibilities:
Requesting Data: The Subscriber
uses the Subscription
to request a certain number of data items from the Publisher. This is done through the request(long n)
method provided by the Subscription. The Publisher then sends the requested number of items to the Subscriber.
Cancelling Subscription: The Subscriber
can use the Subscription
to cancel (cancel()
) its subscription to the Publisher at any time. This is important for managing resources and ensuring that the Publisher stops emitting data if the Subscriber is no longer interested.
Backpressure: The Subscription
allows the Subscriber
to implement backpressure, which is a mechanism that allows the Subscriber to control the rate at which it receives data from the Publisher. If the Subscriber becomes overwhelmed by data, it can use backpressure to signal the Publisher to slow down or stop emitting data temporarily.
Combines the roles of both publisher and subscriber. Processors transform, filter, or otherwise process items as they flow through the stream.
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
The Reactive Streams workflow outlines how data flows and is processed through the different components of the reactive stream processing model, as defined by the Reactive Streams Specification. This workflow ensures that asynchronous data processing is efficient, non-blocking, and well-controlled, particularly in scenarios where data producers and consumers operate at different speeds.
High-level description of the Reactive Streams workflow:
Publisher:
Publisher
interface and provides the subscribe()
method to establish a connection with a Subscriber
.Subscriber:
Subscriber
interface and provides methods like onSubscribe()
, onNext()
, onError()
, and onComplete()
.onSubscribe()
method.request(n)
on the Subscription.onNext()
method, the Subscriber processes the items.Subscription:
Subscription
interface and provides methods like request(n)
and cancel()
.Backpressure Handling:
request(n)
on the Subscription.Completion and Error Handling:
onComplete()
on the Subscriber.onError(Throwable)
on the Subscriber.Processor (Optional):
Processor
interface and allows data transformation, filtering, and processing.Operators and Transformations are used to modify, filter, combine, or otherwise transform the data as it flows through the stream. Let’s define these terms:
Operators are used to modify or manipulate the data emitted by a publisher (source) before it reaches a subscriber (consumer). They can perform operations like filtering, mapping, merging, and more.
Operators are typically chained together to create a pipeline of data transformations.
A transformation is a specific type of operator that takes an input stream and produces an output stream where each element has been transformed in some way.
map: Transforms each element emitted by the source stream using a provided function. For example, you can transform a stream of integers to their squares using the map operator.
filter: Filters the elements emitted by the source stream based on a provided predicate. Only elements that satisfy the predicate are allowed to pass through.
flatMap: Applies a function to each element emitted by the source stream, resulting in multiple output elements for each input element. The output elements are then merged into a single output stream.
merge: Combines multiple streams into a single output stream, interleaving the elements as they arrive from different sources.
concat: Concatenates multiple streams in a sequential manner, ensuring that the elements from the first stream are emitted before the elements from the second stream, and so on.
reduce: Aggregates the elements emitted by the source stream using an accumulator function, producing a single output element.
These are just a few examples, and there are many more operators and transformations available in Reactive Streams libraries like Reactor or RxJava.
The interfaces available in JDK >= 9 java.util.concurrent.Flow
, are 1:1 semantically equivalent to their respective Reactive Streams counterparts.
This means that there will be a migratory period, while libraries move to adopt the new types in the JDK
Get Started | Paradigms | Reactive Programming | Java | Java9