質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

Q&A

解決済

3回答

9268閲覧

Servlet 3.0 非同期レスポンスの使いどころ

yuba

総合スコア5568

Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

2グッド

3クリップ

投稿2016/07/06 16:17

JavaEE Servlet 3.0から登場した非同期レスポンス startAsync() ですが、これがどういう役に立つのかいまひとつ理解できていません。

よくある例ではチャットアプリをCometで実現する、そのとき相手からの発言を待つコードをstartAsyncに渡す、なんていうのが解説されています。が、別にそんなことしなくても同期的にdoGetの中で相手からの発言を一定時間待つと言うコードを書くので同じことができるわけで。

startAsyncを使うと何かのリソースを節約できるのか? そういうことのような気もしますが、同期的にやっても非同期でやっても同じくスレッドが一本wait状態になるだけで、リソースの使用状況は同じに見えます。

startAsyncはどのような使い道のときに何を改善したくて導入されたものなのでしょうか。

A-pZ, BlueMoon👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

コネクション毎にスレッドが必要だったのを、リクエスト毎にスレッドがあれば良いようにした認識です。

Asynchronous processing support in Servlet 3.0 | JavaWorld
にある、

When a connection is idle between requests, the thread can be recycled, and the connection is placed in a centralized NIO select set to detect new requests without consuming a separate thread.

の様に、selectを使う、所謂ノンブロッキングI/Oを使うことで、1スレッドで待ち受けて、リクエストがあった場合にスレッドを割り当てることが出来るようになったということかと思います。

結局裏でワーカースレッドが待機することになります。環境全体で待機しているスレッド数には(つまりコストには)変わりがないのでは? という主旨の質問になります。

については、待機するスレッドは1つなので、必要とするスレッド数は小さくなるということです。

仕様側には実装方法は書かれていないようですが、目的としては以下の部分かと思います。
サーブレット3.0仕様書邦訳版
2.3.3.3 非同期処理(Asynchronous processing)

時にはフィルタ及び/あるいはサーブレットが応答発生前にあるリソースあるいはイベントを待たない と、ある要求処理を終了できないことがある。例えば、あるサーブレットは応答を発生させる前に、 利用可能な JDBC 接続、リモートのウェブ・サービスからの応答、JMS メッセージ、あるいはアプリ ケーションのあるイベント、などを待つ必要がある。そのサーブレット内での待機は非効率な使い方 である。何故ならそれはスレッドと他の限られたリソースを消費するブロッキング動作であるからであ る。しばしばデータベースのような遅いリソースが多くのスレッドをアクセス待ちの状態でブロックさ せ、ウェブ・コンテナ全体のスレッドの枯渇とサービス品質劣化をもたらす。

サーブレット第 3 版では要求の非同期処理を導入しており、そのスレッドをコンテナに返して他のタ スクを実行できるようにしている。


(2016/07/07 15:30 追記)
上に書いた回答は不十分だったと思います。
(というより、仕様に書いてある以上のことを、私が勝手に期待した感じです。。)

AsyncContext.startAsync、AsyncContext.startするのは、目的としては
上記のサーブレット3.0仕様書の2.3.3.3 非同期処理のところにある通りのようですが、その効果は
「HTTP処理用のスレッドをつかみっぱなしにしないこと」の様です。
その結果として考えられるメリットは以下かと思います。

直接的なメリット(効果小)

HTTP処理用のスレッド数は通常なんらかの設定により上限があるため、
この制限とは別の管理のスレッドに処理が渡せることは、ある程度のメリットがあると思います。
とはいえ、実際の総スレッド数としては減るわけでもなくリソースの削減効果もないので、
大したメリットとは思えません。

間接的なメリット(効果大だが想定される使い方か不明)

こちらは、私の考える実装の可能性で、検証したわけでもどこかに書いてあったわけでもないですが、
処理が分離されることにより、ノンブロッキングI/Oを用いた通信を行うことによる
スレッド数の削減ができる前提が一つ満たされると思います。

例えば、指定した外部のWeb APIを呼び出して結果を取得する処理をノンブロッキングI/Oを用いて
複数のリクエストを1スレッドで同時に処理し、
結果が取れた場合のみにAsyncContext.startを呼び出して結果を返すようにできれば、
外部のWeb APIを呼び出して結果を待つ間は、複数のリクエストがあっても1スレッドしか使いません。

今までは、もしこうした処理を実装したとしても、HTTP処理用のスレッドで
処理を返さなければいけなかったので、使えませんでしたが、
AsyncContext.startAsyncの仕組みを使うことで、実装する余地は有るようになったと思います。


(2016/07/06 7:10追記)
上に記述した内容を補強する情報がありました。

Servlet 3.1でのノンブロッキングI/O対応

Servlet 3.1において、startAsyncをベースにして、リクエストやレスポンスのI/OをノンブロッキングI/OにするAPIが追加されています。

以下が解説記事です。
Oracle Blogs 日本語のまとめ: [Java] Non-blocking I/O using Servlet 3.1: Scalable applications using Java EE 7 (TOTD #188)

以下が仕様の追記部分の一部です。
Java Servlet Specification Version 3.1
3.7 Non Blocking IO

Non-blocking request processing in the Web Container helps improve the ever increasing demand for improved Web Container scalability, increase the number of connections that can simultaneously be handled by the Web Container. Non-blocking IO in the Servlet container allows developers to read data as it becomes available or write data when possible to do so. Non-blocking IO only works with async request processing in Servlets and Filters (as defined in Section 2.3.3.3, “Asynchronous processing” on page 2-10), and upgrade processing (as defined in Section 2.3.3.5, “Upgrade Processing” on page 2-20).

ノンブロッキングな外部サーバ呼び出しを行う例

以下のサイトで、ノンブロッキングな外部サーバ呼び出しを行うコード例がありました。こちらで動作確認はしてませんが、処理実装のイメージは、私のイメージ通りです。

Async Servlets - Jayway

lang

1protected void doGet(final HttpServletRequest req, HttpServletResponse resp) { 2 // Initialize async processing. 3 final AsyncContext context = req.startAsync(); 4 5 // This call does not block. 6 client.callExternalService( 7 // This callback is invoked after the external service responds. 8 new Callback<string>() { 9 public void callback(String result) { 10 ServletResponse response = context.getResponse(); 11 response.setContentType("text/plain"); 12 response.setCharacterEncoding("UTF-8"); 13 byte[] entity = ("Result: " + result + ".n").getBytes(Charset.forName("UTF-8")); 14 response.setContentLength(entity.length); 15 try { 16 response.getOutputStream().write(entity); 17 } catch (IOException e) { 18 // Ignored. 19 } 20 context.complete(); 21 } 22 }); 23}

投稿2016/07/07 01:17

編集2016/07/07 22:14
eripong

総合スコア1546

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/07/07 03:04

なるほど、selectを使うということでちょっとストーリーが見えてきました。 1. selectを使わない方式だとリクエストごとにスレッドを立てる(かスレッドプールから払い出す)必要があり、マルチスレッディングのオーバーヘッドが大きかった 2. そこで、1本か少数のスレッドでselect監視し複数のリクエストを順次処理する形にしたい 3. しかしノンブロッキングI/Oだと、重いリクエスト処理に当たるとその1本しかないスレッドがブロックしてしまい他のリクエストが処理されなくなる 4. だから重いリクエスト処理だけは他スレッドに回せるようにした。そのAPIがstartAsync() 重いリクエスト処理ひとつひとつがスレッドを使う点は従来型と変わらないけれど、メインのリクエスト処理が1つか少数のスレッドに収まることで全体としてはスレッド処理が減る、そういうメリットであると。 この理解でいかがでしょうか。
eripong

2016/07/07 04:59

コメントいただいて改めて調べてみたところ、申し訳ありませんが私の理解が誤っていたようです。 夜になってしまいそうですが、調べなおした結果を追記します。今のところの結論としては、startAsyncは、あまり意味なさそうです。 yubaさんがコメントした、「1本か少数のスレッドでselect監視し複数のリクエストを順次処理」する形については、実際そうなっている可能性はありますが、接続を受け付けたら(重いリクエスト処理だけでなく、)全て別スレッドに引き継ぐ形になっているのではと思います。過去、WebLogicやTomcatでそうなっていることは確認したことがあります。
eripong

2016/07/07 06:32

更新しました。ちょっと分かりにくいかもしれないので、何かあればぜひコメントください。
yuba

2016/07/07 14:17

追加の解説をいただき、こちらでも考えてみてだいぶ理解が深まってきました。 まず、よくあるサンプルコードでは必ずAsyncContext.startを呼んでいるのでそれに惑わされていた気がします。startを呼んで何かのコードをワーカースレッドに渡しているのでは、環境内の待機スレッド総数は変わらず、上記の効果小のメリットしかありません。 しかしノンブロッキングI/Oとはそういうものではなかったわけです。そもそも待機というものをしない。重いI/O処理はOSに投げてしまってそこでスレッドは放棄。I/Oの結果が返ってきたらその時点でシグナルが発生するので改めてイベントハンドラが続きをやれば良いという考え方。 つまり非同期サーブレットの正道の使い方はAsyncContext.startを使うことではなく、asyncStartでコンテキストができたらそのままI/O命令を非同期で発行し、完了時ハンドラだけ指定しておいてスレッドを放棄する(doGetをreturnで抜ける)ことなわけです。AsyncContext.completeを呼ぶのは完了時ハンドラの仕事。 これなら、スレッド1本でも複数リクエストを回せます。これは効果大です。
eripong

2016/07/07 22:16

今、回答を追記しましたが、yubaさんのコメント通りかと思います。
yuba

2016/07/07 22:58

完全解決です。
guest

0

解説する記事を見つけました。

JavaEE使い方メモ(Servlet・JSP・EL式)

中ほどに”非同期処理の実装”の記述が有ります。doGet()を抜けた後にresponseが返せる(doGetによるservlet待機のコストが減らせる)のがメリットなのかと思いました。

コンテンツ中に以下の記載が有ります。

非同期処理にするメリットは、サーブレットの実行に割り当てられているスレッドをコンテナがさっさと回収して、別のリクエストの処理に割り当てられて無駄が少なくなる、という点。

メモに書かれた通り、裏で実行・完了待ちしているスレッドが存在する点は変わりないと思います。非同期スレッドを実行させたままdoGetメソッドを終了させてコンテナにサーブレット実行リソースを回収出来るのであれば、非同期スレッドの処理時間に複数リクエストを受けるケースでリソースの利用が効率的になるのではないかと考えました。コンテナに開放出来るスレッドやリソースの種類など分からず、確証の有る回答にならず済みません。

投稿2016/07/07 00:35

編集2016/07/07 02:24
BlueMoon

総合スコア1339

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/07/07 00:39

doGetを呼んでいるスレッドは確かに待機しなくて済むようになりますが、結局裏でワーカースレッドが待機することになります。環境全体で待機しているスレッド数には(つまりコストには)変わりがないのでは? という主旨の質問になります。
BlueMoon

2016/07/07 02:25

ご質問主旨に届いてない回答で済みません。コンテンツを読み直し、理解した点を追記しました。
guest

0

startAsync()

ほほー、こんなことできるようになったんですね
最近、Javaは離れ気味なので追えてないなぁ・・・というのはいいとして

同期的にdoGetの中で相手からの発言を一定時間待つ

これ昔、(2000年頃)こういうのなんか名前ついてたなー、、、なんて言ったっけ?・・・
ってググってたら質問文にCometって書いてありましたね。

で、このComet、出てきた頃は、反則技だとか新しいWebだとかPushだとか色々言われてましたが、
しばらくしてすっかりきかなくなりましたねぇ。

まぁ同期的に待ってしまうと、いつまで待てばいいのとか、張りっぱなしじゃ
コネクションいくらあっても足りないじゃんとかそういうことから出てきたものだと思います。

時は流れて、ネットユーザーが爆発的に増えて、Twitterとかそんなのが出てきて
C1000k問題とかで、Node.jsがそれを解決だ!、、、みたいな流れで
遅ればせながらJavaEEも対応したのかなぁ・・・なんて思いますが。

全然頓珍漢だったらゴメンナサイ 老人の追憶です ^_^;

投稿2016/07/06 17:04

Mr_Roboto

総合スコア2208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/07/06 23:28

同期的に待ってもタイムアウトは可能ですし、コネクションを維持するのは非同期でも同じですよね。
Mr_Roboto

2016/07/07 01:01

なるほど、言われてみればそうですね、、、 ということで、エンジニアたるもの頭で考えることも大事だけど手を動かして試せってことで、私は今から試してみます。なにか分かったらまたコメントしますね ^^
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問