An introduction to reactive streams backpressure response flow programming

concept

One of the reasons why node.js is widely popular is that node.js adopts non-blocking asynchronous programming technology, so that node.js can improve the performance of the server and easily handle a large number of concurrent requests.

But there are many problems in asynchronous programming, such as callback hell, error handling, etc. The streaming programming model is proposed to reduce the difficulty of asynchronous programming. Many Java stream programming class libraries and framework applications are born.
React streams is a back pressure responsive programming specification, which makes asynchronous stream programming relatively standardized and unified through a set of minimal interfaces, methods and protocols. Among them, the reactive stream in jdk9 is the java implementation of react streams.

streaming programming

Streaming programming is a programming paradigm that treats data as a stream rather than a one-off collection of data. The core idea of ​​streaming programming is to treat data collection as a series of elements, rather than a one-time collection of data.
The data processing process is the processing process of the data stream. Data flows from one link to another like a water flow, each link processes the data, and finally gets the final result.
Instead of calling other object methods through a method to complete data operations like the previous programming paradigm.
The underlying idea adopted by streaming programming is the subscriber-publishing model. The data processing link subscribes to data in the form of subscribers. After the data is generated, the publisher will push the data to the subscribers.
Subscribers process data asynchronously, rather than processing all data at once. An article I wrote about the observer pattern can help to better understand this programming pattern.

back pressure

Back pressure refers to a coordination mechanism between data producers and consumers. When data producers produce data faster than consumers consume data, back pressure occurs. The backpressure protocol of react streams stipulates that when the subscriber cannot process the data, the publisher should suspend the publication of data.
Publishers should continue publishing data while subscribers can process the data. In this way, the speed between the producer and the consumer of the data can be guaranteed to be consistent and data loss can be avoided.
It can be understood as an adaptive data feedback publishing mechanism based on the processing capabilities of subscribers.

core

The core of react streams is Publisher, Subscriber, Subscription, Processor.

  • Publisher: The publisher is responsible for publishing data. The publisher can be a data source or a data processor.
  • Subscriber: Subscriber, which is responsible for subscribing data, the subscriber can be a data processor or a data terminal.
  • Subscription: Subscription, which is responsible for subscribing to the publisher's data, and the subscriber can subscribe to multiple publishers.
  • Processor: Processor, which is responsible for processing data. The processor can be a data source or a data terminal.

By using these four core libraries provided by the class library, or customizing your own specific implementation, you can easily use back pressure responsive programming.

publisher

public interface Publisher<T> {
    
    
    void subscribe(Subscriber<? super T> s);
}

Publisher is a publisher responsible for publishing data. The publisher can be a data source or a data processor.
The subscribe method is the entry point for the subscriber to subscribe to the publisher.
You can implement the publisher interface, where jdk9 provides some common implementations of the Flow.Publisher interface, such as:


public class MyPublisher implements Publisher<String> {
    
    
    @Override
    public void subscribe(Subscriber<? super String> s) {
    
    
        s.onSubscribe(new MySubscription(s));
    }
}

Commonly used publisher implementations provided by jdk9

  • SubmissionPublisher: A submittable publisher that can submit data through the submit method, and when the data is submitted, it will push the data to subscribers.

subscriber

public interface Subscriber<T> {
    
    
    void onSubscribe(Subscription s);
    void onNext(T t);
    void onError(Throwable t);
    void onComplete();
}

Subscriber is a subscriber, which is responsible for subscribing data. The subscriber can be a data processor or a data terminal.
The onSubscribe method among them is that after the subscriber subscribes to the publisher, the publisher will call the subscriber's onSubscribe method to pass the subscriber's subscription object to the subscriber.
The onNext method among them is that when the publisher publishes the data, it will call the subscriber's onNext method to pass the data to the subscriber.
The onError method among them is that when the publisher publishes data with an exception, it will call the subscriber's onError method to pass the exception to the subscriber.
The onComplete method among them is that when the publisher finishes publishing the data, it will call the subscriber's onComplete method to notify the subscriber that the data publishing is completed.
In general, you need to implement the subscriber interface by yourself to complete the data processing, such as:

public class MySubscriber implements Subscriber<String> {
    
    
    private Subscription subscription;
    @Override
    public void onSubscribe(Subscription s) {
    
    
        this.subscription = s;
        this.subscription.request(1);
    }
    @Override
    public void onNext(String s) {
    
    
        System.out.println("onNext: " + s);
        this.subscription.request(1);
    }
    @Override
    public void onError(Throwable t) {
    
    
        t.printStackTrace();
    }
    @Override
    public void onComplete() {
    
    
        System.out.println("onComplete");
    }
}

subscription

public interface Subscription {
    
    
    void request(long n);
    void cancel();
}

Subscription is a subscription, which is responsible for subscribing to the publisher's data, and the subscriber can subscribe to multiple publishers.
The n parameter in the request method is the number of data that the subscriber requests the publisher to publish.
The cancel method is to facilitate the subscriber to unsubscribe the publisher's data.

processor

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
    
    
}

Processor is a processor, which is responsible for processing data. The processor can be a data source or a data terminal.
It is not difficult to see from the definition of the interface that a Processor is a combination of a publisher and a subscriber, which can either publish data or subscribe to data.

public class MyProcessor implements Processor<String, String> {
    
    
    private Subscriber<? super String> subscriber;
    @Override
    public void subscribe(Subscriber<? super String> s) {
    
    
        this.subscriber = s;
        this.subscriber.onSubscribe(new MySubscription(s));
    }
    @Override
    public void onSubscribe(Subscription s) {
    
    
        s.request(1);
    }
    @Override
    public void onNext(String s) {
    
    
        System.out.println("onNext: " + s);
        this.subscriber.onNext(s);
    }
    @Override
    public void onError(Throwable t) {
    
    
        t.printStackTrace();
    }
    @Override
    public void onComplete() {
    
    
        System.out.println("onComplete");
    }
}

When the publisher generates data, it can only flow to one subscriber.
If you need to receive data from multiple subscribers in sequence, you need to use a processor. The processor can be used as an intermediate processing and delivery link, because it can be a publisher or a subscriber.

comprehensive writing

import java.io.IOException;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;

/**
 * @Author zhangshiyu
 * @Date 2023/1/26 18:25
 * @project reactive-stream-demo
 */
public class ReactiveStreamDemo {
    
    
    //main
    public static void main(String[] args) throws IOException {
    
    

        //创建发布者
        SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
        //创建订阅者
        Flow.Subscriber<String> subscriber = new Flow.Subscriber<String>() {
    
    
            @Override
            public void onSubscribe(Flow.Subscription subscription) {
    
    
                //订阅者处理订阅请求
                System.out.println("onSubscribe");
                subscription.request(Integer.MAX_VALUE);
            }

            @Override
            public void onNext(String s) {
    
    
                //订阅者处理发布者发布的消息
                System.out.println("onNext----\r\n" + s);
            }

            @Override
            public void onError(Throwable throwable) {
    
    
                //订阅者处理发布者发布的异常消息
                System.out.println("onError");
            }

            @Override
            public void onComplete() {
    
    
                //订阅者处理发布者发布的完成消息
                System.out.println("onComplete");
            }
        };
        Flow.Processor<String, String> processor = new Flow.Processor<String, String>() {
    
    
            private Flow.Subscriber<? super String> subscriber;

            @Override
            public void subscribe(Flow.Subscriber<? super String> subscriber) {
    
    
                this.subscriber = subscriber;
            }

            @Override
            public void onSubscribe(Flow.Subscription subscription) {
    
    
                subscriber.onSubscribe(subscription);
            }

            @Override
            public void onNext(String item) {
    
    
                System.out.println("processor----" + item);
                subscriber.onNext(item + "(processed)");
            }

            @Override
            public void onError(Throwable throwable) {
    
    
                subscriber.onError(throwable);
                System.out.println("processor----\r\n" + throwable.getMessage());
            }

            @Override
            public void onComplete() {
    
    
                subscriber.onComplete();
                System.out.println("processor----\r\n" + "onComplete");
            }
        };
        //发布者和订阅者建立订阅关系
        processor.subscribe(subscriber);
        publisher.subscribe(processor);


        //发布者发布消息
        publisher.submit("hello");
        publisher.submit("world");
        publisher.submit("reactive");
        publisher.close();
        System.in.read();
    }
}

Guess you like

Origin blog.csdn.net/aofengdaxia/article/details/128805103