最近在開發中使用akka http進行請求,返回的是一個future,並且要對future進行超時設置,不知怎么設置,因此學習了下。
一、Future阻塞
首先,scala中的future不支持內置超時,要想達到這樣的目的,可以使用Await進行阻塞,具體例子如下:
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
lazy val f = future { Thread.sleep(2000); true }
Await.result(f, 1 second)
上面的代碼將超時,報如下錯誤:
java.util.concurrent.TimeoutException: at scala.concurrent.impl.Promise $ DefaultPromise.ready(Promise.scala:219) at scala.concurrent.impl.Promise $ DefaultPromise.result(Promise.scala:223) at scala.concurrent.Await $$ anonfun $ result $ 1.apply(package.scala:107) at scala.concurrent.BlockContext $ DefaultBlockContext $ .blockOn(BlockContext.scala:53) ...
二、非阻塞Future超時
但是,我們知道,在future上設置阻塞不是官網推薦的一種方式,因為這會浪費一個線程。因此,我們可以使用akka after實現一種非阻塞式的future超時:
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import akka.actor.ActorSystem
import akka.pattern.after
val system = ActorSystem("theSystem")
lazy val f = future { Thread.sleep(2000); true }
lazy val t = after(duration = 1 second, using = system.scheduler)(Future.failed(new TimeoutException("Future timed out!")))
val fWithTimeout = Future firstCompletedOf Seq(f, t)
fWithTimeout.onComplete {
case Success(x) => println(x)
case Failure(error) => println(error)
}
但是,注意了,為了確保在執行前,計時還沒有開始,必須將after設置lazy val。
但是上述這種模式的缺點是它依賴於akka,因此,我們可以使用純scala模式,來模仿實現after的功能
為了更容易使用future的超時設置,我們可以使用隱式類來擴展scala future從而支持超時:
import scala.concurrent._
import scala.concurrent.duration.FiniteDuration
import ExecutionContext.Implicits.global
import akka.actor.ActorSystem
import akka.pattern.after
implicit class FutureExtensions[T](f: Future[T]) {
def withTimeout(timeout: => Throwable)(implicit duration: FiniteDuration, system: ActorSystem): Future[T] = {
Future firstCompletedOf Seq(f, after(duration, system.scheduler)(Future.failed(timeout)))
}
}
現在,我們可以隨時很方便的給future設置超時了:
import scala.concurrent._
import scala.concurrent.duration._
import scala.util.{ Success, Failure }
import ExecutionContext.Implicits.global
import akka.actor.ActorSystem
implicit val system = ActorSystem("theSystem")
implicit val timeout = 1 second
lazy val f = future { Thread.sleep(2000); true }
f withTimeout new TimeoutException("Future timed out!") onComplete {
case Success(x) => println(x)
case Failure(error) => println(error)
}