이 질문의 요구 사항을 충족하기 위해, 그것의 실행 시간이 최대 기간을 초과하는 경우 장기 실행 계산을 방해 할 수있는 필요가있다. 또한 컨트롤러 작업에서 이러한 중단의 가능성을 다루는 것이 필요합니다.
계산이 여러 단계 및/또는 반복을 포함한다고 가정하면 계산 결과를 포기하고 계산을 실행하는 대신이 계산을 인터럽트하는 한 가지 방법은 계산의 현재 지속 시간이 최대 값보다 큰지 주기적으로 확인하는 것입니다 지속.
이 계산이 실패 할 수 있음을 명시하기 위해 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"))
}
}
예제는 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 { 포크 조인 실행기 { 병렬 팩터 = 200 } } = 20 병렬 맥스} 바로 – Labra