web-dev-qa-db-ja.com

Spring MVCでサーブレット3.1を使用するにはどうすればよいですか?

利用可能な2つの異なる機能があります。

  1. servlet 3.は、コンテナスレッドとは異なるスレッドでリクエストを処理できます。

  2. サーブレット3.1スレッドの読み取り/書き込みをブロックせずにソケットへの読み取り/書き込みを許可します

インターネットには、サーブレット3.0機能に関する多くの例があります。 Springでとても簡単に使用できます。 DefferedResultまたはCompletableFutureを返すだけです

しかし、私は春にサーブレット3.1の使用例を見つけることができません。私の知る限り、WriteListenerReadListenerを登録し、内部でダーティな作業を行う必要があります。しかし、私はそのリスナーの例を見つけることができません。簡単なことではないと思います。

リスナー実装の説明とともに、春のサーブレット3.1機能の例を提供していただけませんか?

15
gstackoverflow

いくつかの例を追うのは難しくありません。 IBMから WASdev/sample.javaee7.servlet.nonblocking で見つけました。 SpringまたはSpring Bootで_javax.servlet_ APIを操作することは、HttpServletRequestまたはHttpServletResponseを注入するようにSpringに要求することだけです。したがって、簡単な例は次のようになります。

_@SpringBootApplication
@Controller
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping(path = "")
    public void writeStream(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletOutputStream output = response.getOutputStream();
        AsyncContext context = request.startAsync();
        output.setWriteListener(new WriteListener() {
            @Override
            public void onWritePossible() throws IOException {
                if ( output.isReady() ) {
                    output.println("WriteListener:onWritePossible() called to send response data on thread : " + Thread.currentThread().getName());
                }
                context.complete();
            }
            @Override
            public void onError(Throwable t) {
                context.complete();
            }
        });
    }
}
_

これは単にWriteListenerを作成し、それを要求出力ストリームに添付して戻ります。派手なものは何もありません。

編集:ポイントは、データをブロックせずに書き込むことができる場合、サーブレットコンテナ(Tomcatなど)がonWritePossibleを呼び出すことです。この詳細については サーブレット3.1を使用した非ブロッキングI/O:Java EE 7(TOTD#188))を使用したスケーラブルなアプリケーション を参照してください。

リスナー(およびライター)には、コンテンツを読み取ることができる場合、またはブロックせずに書き込むことができる場合に呼び出されるコールバックメソッドがあります。

したがって、onWritePossibleは、ブロックせずに_out.println_を呼び出せる場合にのみ呼び出されます。

SetXXXListenerメソッドを呼び出すと、従来のI/Oの代わりに非ブロックI/Oが使用されます。

多分あなたがしなければならないことは、バイトを書き続けることができるかどうか知るために_output.isReady_をチェックすることです。ブロックサイズについて、送信者/受信者と何らかの暗黙の合意が必要になるようです。私はそれを使用したことがないのでわかりませんが、Springフレームワークでこれの例を要求すると、それが提供されます。

したがって、onWritePossibleは、out.printlnをブロックせずに呼び出すことができる場合にのみ呼び出されます。 それは正しいように聞こえますが、どのようにして何バイトを書き込むことができるかをどのように理解できますか?これをどのように制御すればよいですか?

編集2:それは私があなたに正確な答えを与えることができない非常に良い質問です。サーバーがメインサーブレットとは別の(非同期)スレッドでコードを実行すると、onWritePossibleが呼び出されると想定します。例からinput.isReady()またはoutput.isReady()を確認すると、送信者/受信者がさらに準備ができるまでスレッドがブロックされると思います。これは非同期で行われるため、サーバー自体はブロックされず、他の要求を処理できます。私はこれを使用したことがないので、私は専門家ではありません。

ブロックサイズについて送信者/受信者と何らかの暗黙の合意があると私が言ったとき、それは、受信者が1024バイトブロックを受け入れることができる場合、_output.isReady_がtrueのときにその量を書き込むことを意味します。ドキュメンテーションを読むことでそれについての知識がなければなりません、それについてのAPIには何もありません。それ以外の場合は1バイトを書き込むことができますが、Oracleの例では1024バイトのブロックを使用しています。 1024バイトブロックは、ストリーミングI/Oのかなり標準的なブロックサイズです。上記の例は、Oracleの例に示されているように、whileループにバイトを書き込むように拡張する必要があります。それは読者に残された演習です。

プロジェクトリアクターとSpring Webfluxにはbackpressureの概念があり、これをより注意深く扱うことができます。それは別の質問になるだろうし、私はそれが送信者と受信者をどのように結合するか(またはその逆)については詳しく調べていない。

0
K.Nicholas