1. 序章

この簡単なチュートリアルは、SpringMVC5.xxでのいくつかの非同期オブジェクトとストリーミングオブジェクトの使用法を示しています。

具体的には、次の3つの主要なクラスを確認します。

  • ResponseBodyEmitter
  • SseEmitter
  • StreamingResponseBody

また、JavaScriptクライアントを使用してそれらを操作する方法についても説明します。

2.  ResponseBodyEmitter

ResponseBodyEmitterは非同期応答を処理します。

また、これはいくつかのサブクラスの親を表します。そのうちの1つを以下で詳しく見ていきます。

2.1. サーバ側

ResponseBodyEmitter を専用の非同期スレッドと一緒に使用し、 ResponseEntity でラップすることをお勧めします(エミッターを直接注入できます)。

@Controller
public class ResponseBodyEmitterController {
 
    private ExecutorService executor 
      = Executors.newCachedThreadPool();

    @GetMapping("/rbe")
    public ResponseEntity<ResponseBodyEmitter> handleRbe() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        executor.execute(() -> {
            try {
                emitter.send(
                  "/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN);
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return new ResponseEntity(emitter, HttpStatus.OK);
    }
}

したがって、上記の例では、 CompleteableFutures 、より複雑な非同期Promise、または@Asyncアノテーションの使用の必要性を回避できます。

代わりに、非同期エンティティを宣言し、ExecutorServiceによって提供される新しいThreadでラップします。

2.2. クライアント側

クライアント側で使用する場合は、通常のAJAX操作と同じように、単純なXHRメソッドを使用してAPIエンドポイントを呼び出すことができます。

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
       //...
    });
};

xhr('http://localhost:8080/javamvcasync/rbe')
  .then(function(success){ //... });

3. SseEmitter

SseEmitterは、実際にはResponseBodyEmitterのサブクラスであり、すぐに使用できる追加のサーバー送信イベント(SSE)サポートを提供します。

3.1. サーバ側

それでは、この強力なエンティティを活用するコントローラーの例を簡単に見てみましょう。

@Controller
public class SseEmitterController {
    private ExecutorService nonBlockingService = Executors
      .newCachedThreadPool();
    
    @GetMapping("/sse")
    public SseEmitter handleSse() {
         SseEmitter emitter = new SseEmitter();
         nonBlockingService.execute(() -> {
             try {
                 emitter.send("/sse" + " @ " + new Date());
                 // we could send more events
                 emitter.complete();
             } catch (Exception ex) {
                 emitter.completeWithError(ex);
             }
         });
         return emitter;
    }   
}

かなり標準的な料金ですが、これと通常のRESTコントローラーとの間にいくつかの違いがあります。

  • まず、SseEmitterを返します。
  • また、コア応答情報を独自のスレッドでラップします
  • 最後に、 Emitter.send()を使用して応答情報を送信します

3.2. クライアント側

今回は、継続的に接続されているサーバー送信イベントライブラリを利用できるため、クライアントの動作が少し異なります。

var sse = new EventSource('http://localhost:8080/javamvcasync/sse');
sse.onmessage = function (evt) {
    var el = document.getElementById('sse');
    el.appendChild(document.createTextNode(evt.data));
    el.appendChild(document.createElement('br'));
};

4. StreamingResponseBody

最後に、 StreamingResponseBodyを使用して、OutputStreamに直接書き込むことができます。その後、ResponseEntityを使用して、書き込まれた情報をクライアントに返します。

4.1. サーバ側

@Controller
public class StreamingResponseBodyController {
 
    @GetMapping("/srb")
    public ResponseEntity<StreamingResponseBody> handleRbe() {
        StreamingResponseBody stream = out -> {
            String msg = "/srb" + " @ " + new Date();
            out.write(msg.getBytes());
        };
        return new ResponseEntity(stream, HttpStatus.OK);
    }
}

4.2. クライアント側

前と同じように、通常のXHRメソッドを使用して上記のコントローラーにアクセスします。

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
        //...
    });
};

xhr('http://localhost:8080/javamvcasync/srb')
  .then(function(success){ //... });

次に、これらの例のいくつかの成功した使用法を見てみましょう。

5. すべてをまとめる

サーバーを正常にコンパイルし、上記のクライアントを実行した後(提供された index.jsp にアクセス)、ブラウザーに次のように表示されます。

そして、私たちのターミナルでは次のようになります。

 

エンドポイントを直接呼び出して、ストリーミング応答がブラウザーに表示されることを確認することもできます。

6. 結論

FutureCompleteableFutureは、JavaとSpringに強力に追加されたことが証明されていますが、現在、高度に並行するWebアプリケーションの非同期データとストリーミングデータをより適切に処理するためのリソースがいくつかあります。

最後に、GitHubで完全なコード例を確認してください。