web-dev-qa-db-ja.com

Jettyやその他のコンテナは、サーブレットの仕様を守りながら、どのようにNIOを活用しますか?

私はNIOを初めて使用し、JettyがNIOをどのように活用しているかを理解しようとしています。

ブロッキングIOを使用する従来のサーブレットコンテナがリクエストを処理する方法についての私の理解は次のとおりです:

  1. リクエストが届きます
  2. リクエストを処理するためにスレッドが割り当てられ、サーブレットメソッド(doGetなど)が呼び出されます
  3. サーブレットメソッドにはInputStreamOutputStreamが渡されます
  4. サーブレットメソッドはInputStreamから読み取り、OutputStreamに書き込みます
  5. InputStreamOutputStreamは、基本的に、基になるSocketのそれぞれのストリームに関連付けられています。

NIOコネクタを使用した場合の違いは何ですか?私の推測は次の線に沿っています:

  1. リクエストが届きます
  2. JettyはNIOコネクタを使用し、リクエスト全体を非同期でバッファリングします
  3. リクエストが完全に読み取られたら、バッファをInputStreamでラップします
  4. 空の応答バッファを作成します(OutputStreamでラップされます)
  5. スレッドを割り当て、サーブレットメソッド(doGetなど)を呼び出して、上記のラッパーストリームを処理します
  6. サーブレットメソッドは、ラップされた(バッファリングされた)応答ストリームに書き込み、サーブレットメソッドから戻ります
  7. JettyはNIOを使用して、応答バッファーの内容を基になるSocketChannelに書き込みます。

Jettyのドキュメントから、次のことがわかりました:

SelectChannelConnector -このコネクタは、非ブロッキングスレッドモデルで効率的なNIOバッファを使用します。 JettyはDirectNIOバッファーを使用し、リクエストのある接続にのみスレッドを割り当てます。同期はサーブレットAPIのブロッキングをシミュレートし、リクエスト処理の最後にフラッシュされていないコンテンツは非同期に書き込まれます。

Synchronization simulates blocking for the servlet APIの意味がわかりませんか?

22
rationalrevolt

あなたはそれを正確に持っていません。 jettyがNIOコネクタを使用する場合(および9はNIOのみをサポートします)、次のように機能します。

  1. IOアクティビティを探してセレクターを呼び出す、いくつかのスレッド(#コアに応じて1〜4)としてのアイドル状態。これは、Jettyで1,000,000を超える接続に拡張されています。
  2. セレクターがIOアクティビティを検出すると、接続でハンドルメソッドを呼び出します。

    • 他の何かが、この接続のIOを待ってブロックされていることを登録しているため、その場合、セレクターはブロックされた人をウェイクアップします。
    • それ以外の場合は、接続を処理するためにスレッドがディスパッチされます。
  3. スレッドがディスパッチされると、接続の読み取りと解析が試行されます。現在何が起こるかは、接続がhttp、spdy、http2、またはwebsocketのいずれであるかによって異なります。

    • httpの場合、リクエストヘッダーが完了すると、スレッドはコンテンツを待たずにリクエストの処理を呼び出します(最終的にはサーブレットに到達します)。
    • http2/spdyの場合、別のディスパッチが必要ですが、リストのEat-What-You-Kill戦略に関する説明を参照してください: http://dev.Eclipse.org/mhonarc/lists/jetty-dev/msg02166。 html
    • webSocketの場合、メッセージ処理が呼び出されます。
  4. スレッドがサーブレットにディスパッチされると、サーブレットIOがブロックしているように見えますが、HttpInputStreamおよびHttpOutputStreamのレベルの下では、すべてのIOはコールバックと非同期です。ブロッキングAPIは、特別なブロッキングコールバックを使用してブロッキングを実現します。これは、サーブレットが非同期IOの使用を選択した場合、ブロッキングコールバックをバイパスし、非同期APIを多かれ少なかれ直接使用していることを意味します。

  5. サーブレットはrequest.startAsyncを使用して一時停止できます。この場合、ディスパッチされたスレッドはスレッドプールに返されますが、関連付けられた接続はIOに関心があるとマークされません。非同期IOを実行できますが、非同期サイクルが完了したら、スレッドを再割り当てするか、IOアクティビティの接続を再登録するためにAsyncContextイベントが必要です。

このビューは、多重化されたhttp2とspdyによって少し複雑になるため、追加のディスパッチが必要になる可能性があります。

ディスパッチしないHTTPフレームワークは、ベンチマークコードでは非常に高速に実行できますが、データベース、ファイルシステム、RESTサービスなどのブロックなどの愚かなことを実行できる実際のアプリケーションに直面すると、不足します。ディスパッチの意味は、1つの接続がシステム上の他のすべての接続を保持できることを意味します。

Jettyが非同期とディスパッチを処理する方法の詳細については、以下を参照してください。

32
gregw