爲了實現這一問題的要求,這是必要的,如果它的運行時間超過最大持續時間,以便能夠中斷長時間運行計算。此外,有必要處理控制器操作中斷的可能性。
假設計算涉及多個步驟和/或重複,中斷此計算的一種方法(而不是放棄其結果並使計算繼續運行)是定期檢查計算的當前持續時間是否大於最大值持續時間。
爲了明確說明此計算可能失敗,可以聲明它爲返回Try[T]
。
然後,操作可以檢查未來成功時計算嘗試的結果,併爲成功或失敗的嘗試生成適當的輸出。
例如:
package controllers
import play.api._
import play.api.libs.concurrent.Akka
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.mvc._
import play.api.Play.current
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util._
object Application extends Controller {
def factorial(n: Int) = Action.async {
computeFactorial(n, 3.seconds).map { result =>
result match {
case Success(i) => Ok(s"$n! = $i")
case Failure(ex) => InternalServerError(ex.getMessage)
}
}
}
def computeFactorial(n: BigInt, timeout: Duration): Future[Try[BigInt]] = {
val startTime = System.nanoTime()
val maxTime = timeout.toNanos
def factorial(n: BigInt, result: BigInt = 1): BigInt = {
// Calculate elapsed time.
val elapsed = System.nanoTime() - startTime
Logger.debug(s"Computing factorial($n) with $elapsed nanoseconds elapsed.")
// Abort computation if timeout was exceeded.
if (elapsed > maxTime) {
Logger.debug(s"Timeout exceeded.")
throw new ComputationTimeoutException("The maximum time for the computation was exceeded.")
}
// Introduce an artificial delay so that less iterations are required to produce the error.
Thread.sleep(100)
// Compute step.
if (n == 0) result else factorial(n - 1, n * result)
}
Future {
try {
Success(factorial(n))
} catch {
case ex: Exception => Failure(ex)
}
}(Contexts.computationContext)
}
}
class ComputationTimeoutException(msg: String) extends RuntimeException(msg)
object Contexts {
implicit val computationContext: ExecutionContext = Akka.system.dispatchers.lookup("contexts.computationContext")
}
代碼可以是如果它不要求明確標示計算的結果是會犯錯誤的,如果播放的默認異步失敗處理(返回500內部服務器錯誤)更簡潔是足夠了:
object Application extends Controller {
def factorial(n: Int) = Action.async {
computeFactorial(n, 3.seconds).map { i => Ok(s"$n! = $i") }
}
def computeFactorial(n: BigInt, timeout: Duration): Future[BigInt] = {
val startTime = System.nanoTime()
val maxTime = timeout.toNanos
def factorial(n: BigInt, result: BigInt = 1): BigInt = {
if (System.nanoTime() - startTime > maxTime) {
throw new RuntimeException("The maximum time for the computation was exceeded.")
}
Thread.sleep(100)
if (n == 0) result else factorial(n - 1, n * result)
}
Future { factorial(n) }(Akka.system.dispatchers.lookup("contexts.computationContext"))
}
}
實例運行提供了一個線程池是從Play使用用於處理HTTP請求的線程池不同的自定義上下文計算。有關更多信息,請參閱Understanding Play thread pools。上下文是在application.conf
聲明:
contexts {
computationContext {
fork-join-executor {
parallelism-factor=20
parallelism-max = 200
}
}
}
可下載示例,請參見this GitHub project。
我想執行你的代碼,但我得到:[RuntimeException:java.lang.ExceptionInInitializerError],我需要在配置中添加一些東西嗎?我已經嘗試了Play 2.2和Play 2.3 ... – Labra
我更新了答案,並鏈接到示例存儲庫。請下載並讓我知道是否有任何問題。 –
謝謝,我注意到只有我需要做的更改是在conf/application中添加以下行。CONF:上下文{ computationContext { 的fork-join-執行{ 並行性係數= 20 並行-MAX = 200 } } } – Labra