_This article focuses on the JDK9 feature Reactive Stream Responsive Stream, describes what the Reactive Stream is and what the backpressure is, and the interface and two use cases for Reactive Stream provided in JDK9, including how to use Processor.
_1.Reactive Stream concept
_Reactive Stream is a set of standards introduced by JDK9 and a set of data processing specifications based on publish/subscribe mode.Responsive streaming has been an initiative since 2013 to provide an asynchronous stream processing standard for non-blocking backpressure.It is designed to solve the problem of handling element streams -- how to pass element streams from the publisher to the subscriber without blocking the publisher or having the subscriber have an unlimited buffer or discard.More precisely, Reactive streams are designed to "find the smallest set of interfaces, methods, and protocols that describe the operations and entities necessary to achieve the goal of asynchronous streaming of data in a non-blocking backpressure manner".
_Reactive Stream specification was born, which defines the following four interfaces:
The Subscription interface defines how publishers and subscribers are connected The Publisher<T>interface defines the publisher's method The Subscriber<T>interface defines the method of the subscriber Processor<T,R>Interfaces define processors
_Since the Reactive Stream specification was born, RxJava has implemented the Reactive Stream specification since RxJava 2, and the Reactor framework provided by Spring (the basis of WebFlux) has also implemented the Reactive Stream specification successively.
_The following diagram shows the interaction between subscribers and publishers
_2. back pressure concept
_If producers send more messages than consumers can handle, consumers may be forced to keep catching them, consuming more and more resources and burying the potential risk of collapse.To prevent this, a mechanism is needed that allows consumers to notify producers and slow down message generation.Producers can adopt a variety of strategies to achieve this requirement, a mechanism called backpressure.
_Simply put, it is
- Backpressure refers to the interaction between publishers and subscribers
- Subscribers can tell the publisher how much data they need, adjust data traffic, and not cause the publisher to publish too much data, which can waste data or overwhelm subscribers
_3. Implementation of Reactive Stream specification in JDK9
_The implementation specification for Reactive Stream in JDK9 is often referred to as the Flow API, which implements responsive streaming through the java.util.concurrent.Flow and java.util.concurrent.SubmissionPublisher classes
_In JDK9, Reactive Stream's primary interface declaration In the Flow class, the Flow class defines four nested static interfaces that are used to build components of traffic control in which the publisher generates one or more data items for subscribers:
- Publisher: Publisher, Producer of Data Items
- Subscriber: Data item subscribers, consumers
- Subscription: The relationship between publisher and subscriber, subscription token
- Processor: Data Processor
_3.1 Publisher
Publisher publishes data streams to registered Subscriber s.It typically publishes items to subscribers asynchronously using Executor.Publisher needs to ensure that the ubscriber method for each subscription is called strictly in sequence.
-
subscribe: subscriber subscribes to publisher
@FunctionalInterface public static interface Flow.Publisher<T> { public void subscribe(Subscriber<? super T> subscriber); }
_3.2 Subscriber
Subscriber subscribes to Publisher's streams and accepts callbacks.If Subscriber does not make a request, it will not receive data.For a given Subscription, the method of calling Subscriber is strictly sequential.
- onSubscribe: The publisher calls the subscriber's method to deliver the subscription asynchronously, which is executed after the publisher.subscribe method is called
- onNext: The publisher calls this method to pass data to the subscriber
- onError: Call this method when Publisher or Subscriber encounters an unrecoverable error, and then no other method is called
- onComplete: Call this method when the data has been sent and no errors have caused the subscription to terminate, then no other methods will be called
_3.3 Subscription Contract Subscription
Subscription is used to connect Publisher to Subscriber.Subscriber receives items only when requested and can unsubscribe through Subscription.Subscription has two main methods:
-
Request: Subscribers call this method to request data
-
cancel: Subscribers invoke this method to unsubscribe and dissociate subscribers from publishers
public static interface Flow.Subscription { public void request(long n); public void cancel(); }
_3.4 Processor
_Processor is located between Publisher and Subscriber for data conversion.Multiple Processors can be used together to form a processing chain in which the results of the last processor are sent to Subscriber.The JDK does not provide any specific processors.Processors are both subscribers and publishers, and interface definitions inherit both as subscribers and as publishers, receiving data as subscribers, processing it, and publishing it as publishers.
/** * A component that acts as both a Subscriber and Publisher. * * [@param](https://my.oschina.net/u/2303379) <T> the subscribed item type * [@param](https://my.oschina.net/u/2303379) <R> the published item type */ public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> { }
_4. Reactive Stream (Flow API) specification call flow in JDK9
_Publisher is the publisher who can emit elements, and Subscriber is the subscriber who receives elements and responds.When the subscribe method in Publisher is executed, the publisher calls back the subscriber's onSubscribe method, in which the subscriber usually requests n data from the publisher with the help of an incoming Subscription.The publisher then sends out up to N data to the subscriber by constantly calling the subscriber's onNext method.If all the data is sent out, onComplete is called to inform subscribers that the stream has been sent out; if an error occurs, error data is sent through onError and the stream is terminated as well.
_Subscription is the "link" (contract) between Publisher and Subscriber.Because when the publisher calls the subscribe method to register the subscriber, the Subscription object is passed in through the subscriber's callback method onSubscribe, and the subscriber can then "ask" the publisher for data using the request method of the Subscription object.The mechanism of back pressure is based on this.
_5. Case-Response Basic Use Cases
_5.1 The following code briefly demonstrates SubmissionPublisher and the basic usage of this publish-subscribe framework:
_Note to use versions above JDK9
/** * [@author](https://my.oschina.net/arthor) johnny * [@create](https://my.oschina.net/u/192469) 2020-02-24 5:44 p.m. **/ @Slf4j public class ReactiveStreamTest { public static void main(String[] args) throws InterruptedException { //1. Create Producer Publisher JDK9's own implementation of the Publisher interface SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>(); //2. Creating Subscriber requires implementing internal methods on your own Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<>() { private Flow.Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; System.out.println("Subscription succeeded."); subscription.request(1); System.out.println("Request a data in a subscription method"); } @Override public void onNext(Integer item) { log.info("[onNext Receive data item : {}] ", item); subscription.request(1); } @Override public void onError(Throwable throwable) { log.info("[onError Exception occurred)"); subscription.cancel(); } @Override public void onComplete() { log.info("[onComplete All data receipt completed)"); } }; //3.Publishers and subscribers establish a subscription relationship by calling back the subscriber's onSubscribe method to pass in the subscription contract publisher.subscribe(subscriber); //4. Publisher Generates Data for (int i = 1; i <= 5; i++) { log.info("[production data {} ]", i ); //submit is a blocking method, which calls the subscriber's onNext method publisher.submit(i); } //5. When publisher data has been published, close the send and the subscriber's onComplete method will be callback publisher.close(); //Main thread sleeps for a while Thread.currentThread().join(100000); } }
_Print Output Results
_It looks like we can't see what Reactive Stream does, but the key point is publisher.submit(i); submit is a blocking method Let's modify the code a little
1. Add time-consuming operations to onNext to simulate business time-consuming logic 2. Increase the number of publishers publishing data to simulate real-world unlimited data
@Override public void onNext(Integer item) { log.info("[onNext Receive data item : {}] ", item); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } subscription.request(1); } //Publisher Generates Data for (int i = 1; i <= 1000; i++) { log.info("[production data {} ]", i ); //submit is a blocking method, which calls the subscriber's onNext method publisher.submit(i); }
_Direct-view printing
_You will find that the publisher will stop production after generating data for 256 because the publisher.submit(i) method is blocked. There is an internal buffer array with a maximum capacity of 256, only if the subscription.request (1) is sent by the subscriber;After a request, the onNext method takes the data out of the buffer array sequentially and passes it to the subscriber for processing. When the subscription.request(1) method is called, the publisher discovers that the array is not full to reproduce the data, which prevents the producer from generating too much data at once and crushing the subscriber, thereby implementing a backpressure mechanism
_6. Case 2 Responsive Use Case with Processor
_6.1 Create Custom Processor
package com.johnny.webflux.webfluxlearn.reactivestream; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Flow; import java.util.concurrent.SubmissionPublisher; /** * Custom Processor * * @author johnny * @create 2020-02-25 1:56 p.m. **/ @Slf4j public class MyProcessor extends SubmissionPublisher<Integer> implements Flow.Processor<Integer, Integer> { private Flow.Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { log.info("[Processor Receive Subscription Request)"); //Save the subscription relationship and use it to correspond to the publisher this.subscription = subscription; this.subscription.request(1); } @Override public void onNext(Integer item) { log.info("[onNext Receive publisher data : {} ]", item); //Do business processing. if (item % 2 == 0) { //Filter even numbers sent to subscribers this.submit(item); } this.subscription.request(1); } @Override public void onError(Throwable throwable) { // We can tell the publisher that we won't accept the data later this.subscription.cancel(); } @Override public void onComplete() { log.info("[Processor Finished)"); this.close(); } }
_6.2 Run demo to associate publisher with Processor and subscriber
package com.johnny.webflux.webfluxlearn.reactivestream; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Flow; import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.TimeUnit; /** * Case with Processor * * @author johnny * @create 2020-02-25 2:17 p.m. **/ @Slf4j public class ProcessorDemo { public static void main(String[] args) throws InterruptedException { //Create Publisher SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>(); //Creating a Processor is both a publisher and a subscriber MyProcessor myProcessor = new MyProcessor(); //Create Final Subscriber Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<>() { private Flow.Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; this.subscription.request(1); } @Override public void onNext(Integer item) { log.info("[onNext from Processor Receive filtered data item : {}] ", item); this.subscription.request(1); } @Override public void onError(Throwable throwable) { log.info("[onError Exception occurred)"); subscription.cancel(); } @Override public void onComplete() { log.info("[onComplete All data receipt completed)"); } }; //Establish a relationship between the publisher and the processor, where the processor acts as a subscriber publisher.subscribe(myProcessor); //Establish a relationship between the processor and the subscriber where the processor acts myProcessor.subscribe(subscriber); //Publisher Publishes Data publisher.submit(1); publisher.submit(2); publisher.submit(3); publisher.submit(4); publisher.close(); TimeUnit.SECONDS.sleep(2); } }
_7. Summary
_This article focuses on the JDK9 feature Reactive Stream Responsive Stream, describes what the Reactive Stream is and what the backpressure is, and the interface and two use cases for Reactive Stream provided in JDK9, including how to use Processor.
_Just pay attention to the four interfaces provided by JDK9 and the internal methods. Knock through the code for the case is actually a very simple process to refuel!!!
Personal blogging system: https://www.askajohnny.com Welcome! This article is published by blog OpenWrite Release!