リアクティブ ストリームのバックプレッシャー応答フロー プログラミングの概要

コンセプト

node.js が広く普及している理由の 1 つは、node.js がノンブロッキング非同期プログラミング テクノロジを採用しているため、サーバーのパフォーマンスが向上し、大量の同時リクエストを簡単に処理できることです。

しかし、非同期プログラミングには、コールバック地獄やエラー処理など、多くの問題があります。ストリーミング プログラミング モデルは、非同期プログラミングの難しさを軽減するために提案されています。多くの Java ストリーム プログラミング クラス ライブラリとフレームワーク アプリケーションが誕生し、
React ストリームはバック プレッシャー応答型プログラミング仕様であり、最小限のインターフェイス、メソッド、プロトコルのセットを通じて非同期ストリーム プログラミングを比較的標準化および統一化します。このうち、jdk9 の reactive stream は、react streams の Java 実装です。

ストリーミングプログラミング

ストリーミング プログラミングは、データを 1 回限りのデータのコレクションではなくストリームとして扱うプログラミング パラダイムです。ストリーミング プログラミングの中心的な考え方は、データ収集を 1 回限りのデータ収集ではなく、一連の要素として扱うことです。
データ処理工程は、データストリームの処理工程である。データは水の流れのように 1 つのリンクから別のリンクに流れ、各リンクでデータが処理され、最終的に最終結果が得られます。
以前のプログラミング パラダイムのように、メソッドを通じて他のオブジェクト メソッドを呼び出してデータ操作を完了するのではなく。
ストリーミング プログラミングで採用されている基本的な考え方は、サブスクライバー パブリッシング モデルです。データ処理リンクは、サブスクライバーの形式でデータをサブスクライブします。データが生成された後、パブリッシャーはデータをサブスクライバーにプッシュします。
サブスクライバーは、すべてのデータを一度に処理するのではなく、非同期的にデータを処理します。オブザーバー パターンについて私が書いた記事は、このプログラミング パターンをより深く理解するのに役立ちます。

背圧

バック プレッシャーとは、データ プロデューサーとコンシューマーの間の調整メカニズムを指します。データ プロデューサーが、コンシューマーがデータを消費するよりも早くデータを生成すると、バック プレッシャーが発生します。反応ストリームのバックプレッシャー プロトコルでは、サブスクライバーがデータを処理できない場合、パブリッシャーはデータの公開を一時停止する必要があると規定しています。
サブスクライバーがデータを処理できる間、パブリッシャーはデータの公開を継続する必要があります。このようにして、データのプロデューサーとコンシューマー間の速度の一貫性が保証され、データ損失が回避されます。
これは、加入者の処理能力に基づいた適応型データ フィードバック公開メカニズムとして理解できます。

反応ストリームの中核は、パブリッシャー、サブスクライバー、サブスクリプション、プロセッサーです。

  • パブリッシャー: パブリッシャーはデータのパブリッシュを担当します。パブリッシャーはデータ ソースまたはデータ プロセッサーになることができます。
  • サブスクライバ: データのサブスクライブを担当するサブスクライバ。サブスクライバは、データ プロセッサまたはデータ端末にすることができます。
  • サブスクリプション: サブスクリプション。パブリッシャーのデータをサブスクライブする責任があり、サブスクライバーは複数のパブリッシャーをサブスクライブできます。
  • プロセッサ: データの処理を担当するプロセッサ。プロセッサはデータ ソースまたはデータ端末になります。

クラス ライブラリによって提供されるこれら 4 つのコア ライブラリを使用するか、独自の実装をカスタマイズすることで、バック プレッシャー応答プログラミングを簡単に使用できます。

出版社

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

パブリッシャーは、データの公開を担当するパブリッシャーです。パブリッシャーは、データ ソースまたはデータ プロセッサーになることができます。
subscribe メソッドは、サブスクライバーがパブリッシャーをサブスクライブするためのエントリー ポイントです。
パブリッシャー インターフェイスを実装できます。jdk9 では、次のような Flow.Publisher インターフェイスの一般的な実装がいくつか提供されています。


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

jdk9 によって提供される一般的に使用されるパブリッシャー実装

  • SubmissionPublisher: submit メソッドを通じてデータを送信できる、送信可能なパブリッシャー。データが送信されると、サブスクライバーにデータをプッシュします。

加入者

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

サブスクライバは、データのサブスクライブを担当する加入者であり、データ プロセッサまたはデータ端末の場合があります。
このうち onSubscribe メソッドは、サブスクライバーがパブリッシャーをサブスクライブした後、パブリッシャーがサブスクライバーの onSubscribe メソッドを呼び出して、サブスクライバーのサブスクリプション オブジェクトをサブスクライバーに渡します。
このうち onNext メソッドは、パブリッシャーがデータをパブリッシュするときに、サブスクライバーの onNext メソッドを呼び出してデータをサブスクライバーに渡します。
このうち onError メソッドは、パブリッシャーが例外を含むデータをパブリッシュするときに、サブスクライバーの onError メソッドを呼び出して例外をサブスクライバーに渡します。
このうち onComplete メソッドは、パブリッシャーがデータのパブリッシュを完了すると、サブスクライバーの onComplete メソッドを呼び出して、データのパブリッシュが完了したことをサブスクライバーに通知します。
一般に、次のようなデータ処理を完了するには、サブスクライバ インターフェイスを自分で実装する必要があります。

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");
    }
}

サブスクリプション

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

サブスクリプションは、パブリッシャーのデータをサブスクライブする責任を負い、サブスクライバーは複数のパブリッシャーをサブスクライブできます。
request メソッドの n パラメーターは、サブスクライバーがパブリッシャーに公開を要求するデータの数です。
cancel メソッドは、サブスクライバーがパブリッシャーのデータのサブスクリプションを解除しやすくするためのものです。

プロセッサー

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

プロセッサはデータの処理を担当するプロセッサであり、データ ソースまたはデータ端末の場合があります。
インターフェースの定義から、プロセッサーがパブリッシャーとサブスクライバーの組み合わせであり、データをパブリッシュすることも、データをサブスクライブすることもできることを理解するのは難しくありません。

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");
    }
}

パブリッシャがデータを生成する場合、データは 1 つのサブスクライバにのみ送信できます。
複数のサブスクライバから順番にデータを受信する必要がある場合は、プロセッサを使用する必要があります。プロセッサはパブリッシャまたはサブスクライバになることができるため、中間処理および配信リンクとして使用できます。

包括的な執筆

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();
    }
}

おすすめ

転載: blog.csdn.net/aofengdaxia/article/details/128805103