§非同期レスポンスの処理
§非同期レスポンスはなぜ必要か?
これまでは、web クライアントに送信するレスポンスをすぐに生成できることとしていました。しかし、常にこのような場合だけではありません。レスポンスは、高価な計算や長い web サービスの呼び出しに依存するかもしれません。
Play の仕組み上、アクションの実行は可能な限り早く (言い換えると、ノンブロッキングに) 完了しなければなりません。では、レスポンスがまだ生成可能でないときに、一体何を返すべきでしょうか? それは、 レスポンスの Future です!
Future[Result]
は、最終的に Result
型の値を補償します。通常の Result
のかわりに Future[Result]
を返すことで、何もブロックせずに即座に結果を返すことができます。Play は後に約束が果たされたときに、内包された結果を自動的にクライアントへ送信します。
web クライアントはレスポンスを待っている間ずっとブロックされますが、その間でもサーバ側の処理は全くブロックされないため、計算リソースを他のクライアントのために使うことができます。
§Future[Result]
の生成
Future[Result]
を生成するためには、元となる Future 、つまり結果を計算するために必要な値についての Future が先に必要になります。
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
を得る簡単な方法です。
val futureInt: Future[Int] = scala.concurrent.Future {
intensiveComputation()
}
ノート: ここでは、非常に時間のかかる計算を別スレッドで実行しています。その他に、このような計算を Akka remote を利用してバックエンドサーバのクラスタ上で実行することもできます。
§AsyncResult
これまでは SimpleResult
を使ってきましたが、非同期な結果を送信するためには、SimpleResult
をラップする AsyncResult
が必要です。
def index = Action {
val futureInt = scala.concurrent.Future { intensiveComputation() }
Async {
futureInt.map(i => Ok("Got result: " + i))
}
}
ノート:
Async { }
はFuture[Result]
からAsyncResult
を作成するためのヘルパーメソッドです。
§タイムアウト処理
何らかの問題が発生したとき web ブラウザが延々とブロックしてしまうことを避けるために、タイムアウトが役立つことが多々あります。そのような場合、 Promise と Promise のタイムアウトを簡単に組み合わせることができます。
def index = Action {
val futureInt = scala.concurrent.Future { intensiveComputation() }
val timeoutFuture = play.api.libs.concurrent.Promise.timeout("Oops", 2.seconds)
Async {
Future.firstCompletedOf(Seq(futureInt, timeoutFuture)).map {
case i: Int => Ok("Got result: " + i)
case t: String => InternalServerError(t)
}
}
}
次ページ: HTTP レスポンスのストリーミング