§非同期レスポンスの処理
§コントローラーを非同期に
内部的に、Play Framework は完全に非同期処理です。Play は全てのリクエストを非同期で、ノンブロッキングに処理します。
標準設定は非同期のコントローラー用に最適化されています。言い換えると、アプリケーションコードはコントローラーをブロックさせないべきで、すなわち、コントローラーのコードをオペレーションのために待たせることです。ブロックするオペレーションの一般的な例として、JDBC の呼び出し、ストリーミング API 、HTTP のリクエスト、そして長い計算処理に当たります。
より多くの並列のリクエストをコントローラーをブロックすることで可能とする標準の実行コンテキストでスレッド数を増やすことは可能ですが、以下に示す非同期のコントローラを維持するおすすめのアプローチはより簡単にスケールし、システムを低負荷なレスポンスに抑えられます。
§ノンブロッキングなアクションの作成
Play の仕組み上、アクションの実行は可能な限り早く (言い換えると、ノンブロッキングに) 完了しなければなりません。では、レスポンスがまだ生成可能でないときに、一体何を返すべきでしょうか? それは、 レスポンスの Future です!
Future[Result]
は最終的に Result
型の値を埋めます。通常の Result
のかわりに Future[Result]
を返すことで、何もブロックせずに即座に結果を返すことができます。Play は約束が果たされるとすぐに結果を返します。
web クライアントはレスポンスを待っている間ずっとブロックされますが、その間でもサーバ側の処理は全くブロックされないため、計算リソースを他のクライアントのために使うことができます。
§Future[Result]
の生成
Future[Result]
を生成するためには、元となる Future 、つまり結果を計算するために必要な値についての Future が先に必要になります。
import play.api.libs.concurrent.Execution.Implicits.defaultContext
val futurePIValue: Future[Double] = computePIAsynchronously()
val futureResult: Future[Result] = futurePIValue.map { pi =>
Ok("PI value computed: " + pi)
}
Play の全ての非同期処理に関する API 呼び出しは Future
を返します。例えば、play.api.libs.WS
API を使って外部の Web サービスを呼び出す場合や、play.api.libs.Akka
API 経由で Akka を使った非同期タスクを実行したり、アクターと通信したりする場合がそうです。
以下はコードブロックを非同期で実行して Future
を得る簡単な方法です。
import play.api.libs.concurrent.Execution.Implicits.defaultContext
val futureInt: Future[Int] = scala.concurrent.Future {
intensiveComputation()
}
メモ: スレッドのコードは Future と実行されることを理解することが重要です。上記の二つのコードブロックでは、 Play の標準実行コンテキストのインポートがあります。これは implicit parameter で、コールバックを許容している Future API 上の全てのメソッドに渡されます。実行コンテキストはしばしばスレッドプールと等価で、必要ではありません。
魔法のようにに同期の IO を
Future
で内包しても、非同期には変えられません。もしアプリケーションの構造的にオペレーションのブロックを避けられないのであれば、その場合はスレッドをブロックしてオペレーションを実行するしかないでしょう。Future
でオペレーションを囲んだ際に、想定される並列数を扱えるのに十分なスレッドを構成できるような別の実行コンテキストで動かせるように設計する必要があります。詳しくは Play のスレッドプールを理解する を参照してください。ブロッキングオペレーションを扱うのに Actor もまた便利です。 Actor はタイムアウトや失敗、ブロッキング実行コンテキストの設定やサービスに関連した状態を扱うのに完全なモデルです。 Actor はまた、
ScatterGatherFirstCompletedRouter
のような同時的なキャッシュへのアクセスやデータベースへのリクエストやクラスタのバックエンドサーバ上の遠隔の実行もできるようなものを提供しています。しかし Actor は過剰供給でもあるでしょう。
§Future を返す
これまでは、アクションをビルドするために Action.apply
ビルダーメソッドを使ってきましたが、非同期に Result を返すためには Action.async
ビルダーメソッドを使う必要があります
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def index = Action.async {
val futureInt = scala.concurrent.Future { intensiveComputation() }
futureInt.map(i => Ok("Got result: " + i))
}
§アクションは標準で非同期
Play actions は、標準で非同期です。例えば、以下のコントローラーコードを見ると、{ Ok(...) }
部分は、コントローラーのボディのメソッドではありません。Action
型のオブジェクトを作る Action
オブジェクトの apply
メソッドに渡された無名関数です。内部的には、あなたが書いた無名関数はいずれ呼ばれ、結果は Future
に囲まれます。
val echo = Action { request =>
Ok("Got request [" + request + "]")
}
メモ:
Action.apply
とAction.async
は、内部的に同じように扱うAction
オブジェクトを作成しています。非同期の一種類のAction
があり、(同期のものと非同期のもののように)二種類あるわけではありません。.async
ビルダーは、ノンブロッキングコードを簡単に書けるFuture
を返す API ベースの Action を簡単にそして単純に作れます。
§タイムアウト処理
何らかの問題が発生したとき web ブラウザが延々とブロックしてしまうことを避けるために、タイムアウトが役立つことが多々あります。そのような場合、 Promise と Promise のタイムアウトを簡単に組み合わせることができます。
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
def index = Action.async {
val futureInt = scala.concurrent.Future { intensiveComputation() }
val timeoutFuture = play.api.libs.concurrent.Promise.timeout("Oops", 1.second)
Future.firstCompletedOf(Seq(futureInt, timeoutFuture)).map {
case i: Int => Ok("Got result: " + i)
case t: String => InternalServerError(t)
}
}
Next: HTTP レスポンスのストリーミング
このドキュメントの翻訳は Play チームによってメンテナンスされているものではありません。 間違いを見つけた場合、このページのソースコードを ここ で確認することができます。 ドキュメントガイドライン を読んで、お気軽にプルリクエストを送ってください。