前提
この記事はplayframework 2.5.x系を使っている。
最新バージョンである、2.8.xでは、実装が大きく異なるので要注意。
環境
- macOS Mojava 10.14.4
- Scala 2.11.1
- playframework 2.5.x
実装
標準ライブラリでできるタイムアウト処理としては、3通りできるっぽい。
1. play.api.libs.concurrent.Timeoutを使う
公式ドキュメントには、この実装方法が記載されているが、どうやっても、play.api.libs.concurrent.Timeout
が見つからない(importできない)ので、諦めた・・・
2. Futures.timeoutを使う
Futures.timeoutを使って、タイムアウト処理を実装します。
基本的に、playのドキュメントに記載している実装を行う
val response: Future[Int] = Future(1)
val timeoutFuture = Futures.timeout("timeout", 1000L, TimeUnit.MILLISECONDS).asScala
Future.firstCompletedOf(Seq(response, timeoutFuture)).map {
case message: String => { // タイムアウト時の処理 }
case value: Int => {// 正常時の処理}
}
Futures.timeout
は、Javaのライブラリなので、asScalaメソッドでscalaのコードに変換する必要がある。
firstCompletedOf
メソッドは、引数に受け取ったFuture
の中で一番初めに処理が終わった物を返すメソッドである。
参考
3. akka.pattern.afterを使う
2で利用したライブラリはJavaのライブラリなので、scalaで書かれているライブラリで実装してみる。
調べてみるとplay.api.libs.conccurent.Promise
での実装方法が多くヒットする。
しかし以下のように、play.api.libs.conccurent.Promise
のtimeoutメソッドを使おうとすると、play2.5からは、akka.pattern.after
を使うように警告が出る。
@deprecated("Use akka.pattern.after(duration, actorSystem.scheduler)(Future(message)) instead", since = "2.5.0")
def timeout[A](message: => A, duration: scala.concurrent.duration.Duration)(implicit ec: ExecutionContext): Future[A] = {
timeout(message, duration.toMillis)
}
}
akka.pattern.afterを使ってタイムアウト処理を実装してみる。
class Test @Inject()(actorSystem: ActorSystem) {
val delayed = akka.pattern.after(FiniteDuration(500L, TimeUnit.MILLISECONDS), actorSystem.scheduler)(Future.failed(new TimeoutException("timeout")))
val bucketTypeFuture = Future(1)
try {
Future.firstCompletedOf(Seq(delayed, intFuture)).map {
case number: Int => // 正常時の処理
case _ => // 正常だが、意図しないレスポンスの場合の処理
}
} catch {
case e: TimeoutException =>
logger.info(e.getMessage)
// タイムアウト時の処理
}
}
基本的に2と同じような実装内容になっている。