web-dev-qa-db-ja.com

Spring Web-Fluxの背圧メカニズム

私はSpring Web-Fluxのスターターです。次のようにコントローラーを作成しました。

@RestController
public class FirstController 
{
    @GetMapping("/first")
    public Mono<String> getAllTweets() 
    {
        return Mono.just("I am First Mono")
    }
}

事後的な利点の1つはBackpressureであり、要求または応答率のバランスを取ることができることを知っています。 Spring Web-Fluxでバックプレッシャーメカニズムを使用する方法を理解したいと思います。

21
Sam

WebFluxのバックプレッシャー

WebFluxフレームワークの現在の実装でBackpressureがどのように機能するかを理解するために、ここでデフォルトで使用されるトランスポート層を要約する必要があります。覚えているかもしれませんが、ブラウザとサーバー間の通常の通信(通常、サーバー間の通信も同じです)は、TCP接続を通じて行われます。WebFluxは、クライアントと次に、backpressure controlという用語の意味を理解するために、Reactive Streams仕様の観点から背圧の意味を要約する必要があります。

基本的なセマンティクスは、背圧によってストリーム要素の送信がどのように規制されるかを定義します。

したがって、このステートメントから、Reactive Streamsでは、バックプレッシャーは、受信者が消費できる要素の数の送信(通知)を通じて需要を調整するメカニズムであると結論付けることができます。そして、ここで注意が必要な点があります。 TCPには、論理要素の抽象化ではなくバイトの抽象化があります。通常、バックプレッシャー制御と言って欲しいのは、ネットワークとの間で送受信される論理要素の数の制御です。 TCPには独自のフロー制御があります(意味 here およびアニメーション そこ を参照)このフロー制御は、論理要素ではなくバイト用です。 。

WebFluxモジュールの現在の実装では、背圧はトランスポートフロー制御によって調整されますが、受信者の実際の要求を公開しません。インタラクションフローを最終的に確認するには、次の図を参照してください。

enter image description here

簡単にするために、上の図は2つのマイクロサービス間の通信を示しており、左のマイクロサービスはデータのストリームを送信し、右のマイクロサービスはそのストリームを消費します。次の番号付きリストは、その図の簡単な説明です。

  1. これは、論理要素のバイトへの変換と、TCP(ネットワーク)との間の転送および受信を適切に行うWebFluxフレームワークです。
  2. これは、ジョブの完了後に次の要素を要求する要素の長期実行処理の開始です。
  3. ここでは、ビジネスロジックからの要求はありませんが、WebFluxは確認なしでネットワークから送信されるバイトをキューに入れます(ビジネスロジックからの要求はありません)。
  4. TCPフロー制御の性質のため、サービスAは引き続きデータをネットワークに送信できます。

上記の図からわかるように、受信者によって公開される需要は、送信者の需要(ここでは論理要素の需要)とは異なります。これは、両方の需要が分離され、WebFlux <->ビジネスロジック(サービス)の相互作用に対してのみ機能し、サービスA <->サービスBの相互作用のバックプレッシャーが少なくなることを意味します。

すべてのことは、バックプレッシャー制御がWebFluxで期待どおりに公平ではないことを意味します。

しかし、私はまだ背圧を制御する方法を知りたいです

それでもWebFluxでバックプレッシャーを不公平に制御したい場合は、 limitRate() などのProject Reactorオペレーターのサポートでそれを行うことができます。次の例は、その演算子の使用方法を示しています。

_@PostMapping("/tweets")
public Mono<Void> postAllTweets(Flux<Tweet> tweetsFlux) {

    return tweetService.process(tweetsFlux.limitRate(10))
                       .then();
}
_

この例からわかるように、limitRate()演算子を使用すると、一度にプリフェッチされる要素の数を定義できます。つまり、最終的なサブスクライバーが_Long.MAX_VALUE_要素を要求した場合でも、limitRate演算子はその要求をチャンクに分割し、それ以上を一度に消費することはできません。プロセスを送信する要素でも同じことができます。

_@GetMapping("/tweets")
public Flux<Tweet> getAllTweets() {

    return tweetService.retreiveAll()
                       .limitRate(10);
}
_

上記の例は、WebFluxが一度に10個を超える要素を要求した場合でも、limitRate()は要求をプリフェッチサイズに制限し、指定された数を超える要素を一度に消費しないことを示します。

別のオプションは、独自のSubscriberを実装するか、Project ReactorからBaseSubscriberを拡張することです。たとえば、次の例は、その方法の単純な例です。

_class MyCustomBackpressureSubscriber<T> extends BaseSubscriber<T> {

    int consumed;
    final int limit = 5;

    @Override
    protected void hookOnSubscribe(Subscription subscription) {
        request(limit);
    }

    @Override
    protected void hookOnNext(T value) {
        // do business logic there 

        consumed++;

        if (consumed == limit) {
            consumed = 0;

            request(limit);
        }
    }
}
_

RSocketプロトコルによる公平なバックプレッシャー

ネットワーク境界を介して論理要素のバックプレッシャーを実現するには、そのための適切なプロトコルが必要です。幸いなことに、 RScoket protocol と呼ばれるものがあります。 RSocketは、ネットワーク境界を介して実際の需要を転送できるようにするアプリケーションレベルのプロトコルです。 RSocketサーバーのセットアップを可能にするプロトコルのRSocket-Java実装があります。サーバー間通信の場合、同じRSocket-Javaライブラリーもクライアント実装を提供します。 RSocket-Javaの使用方法の詳細については、次の例を参照してください here 。ブラウザーとサーバー間の通信には、WebSocketを介してブラウザーとサーバー間のストリーミング通信を配線できる RSocket-JS 実装があります。

RSocket上の既知のフレームワーク

現在、RSocketプロトコルの上に構築されたいくつかのフレームワークがあります。

プロテウス

フレームワークの1つは、RSocketの上に構築された本格的なマイクロサービスを提供するProteusプロジェクトです。また、ProteusはSpringフレームワークと十分に統合されているため、公正なバックプレッシャー制御を実現できるようになりました(例を参照してください ()

さらに読む

45
Oleh Dokuka