Scala如何使用akka actor有效地处理超时操作

时间:2013-07-11 11:46:54

标签: scala timeout actor

我目前正在使用Rhino在一个宁静的服务中评估javascript脚本。我希望有一个评估时间。 我创建了一个模拟示例actor(使用scala 2.10 akka actors)。

case class Evaluate(expression: String)

class RhinoActor extends Actor {

  override def preStart() = { println("Start context'"); super.preStart()}

  def receive = {
    case Evaluate(expression) ⇒ {
      Thread.sleep(100)
      sender ! "complete"
    }
  }

  override def postStop() = { println("Stop context'"); super.postStop()}
}

现在我按如下方式运行这个演员:

  def run {
    val t = System.currentTimeMillis()
    val system = ActorSystem("MySystem")

    val actor = system.actorOf(Props[RhinoActor])

    implicit val timeout = Timeout(50 milliseconds)
    val future = (actor ? Evaluate("10 + 50")).mapTo[String]

    val result = Try(Await.result(future, Duration.Inf))

    println(System.currentTimeMillis() - t)
    println(result)
    actor ! PoisonPill

    system.shutdown()
  }

在这样的闭包中使用ActorSystem是否明智?它可能同时有请求?

我是否应该将ActorSystem设为全局,并且在这种情况下是否可以?

是否有更合适的替代方法?

编辑:我想我需要直接使用期货,但我需要preStart和postStop。目前正在调查中。 编辑:似乎你没有得到与期货挂钩。

1 个答案:

答案 0 :(得分:2)

我会尽力回答你的一些问题。

首先,ActorSystem是一个非常重的构造。您不应该为每个需要actor的请求创建一个。您应该全局创建一个,然后使用该单个实例来生成您的actor(并且system.shutdown()中不再需要run)。我相信这涵盖了你的前两个问题。

在这里使用actor执行javascript的方法对我来说似乎很有道理。但是,您可能希望在RhinoActor后面聚集一堆Router s而不是为每个请求启动一个actor,每个实例都有自己的rhino引擎,该引擎将在{{1}期间设置}。这样做将消除每个请求的rhino初始化成本,从而加快您的js评估速度。只需确保适当调整池的大小。此外,如果采用这种方法,则无需为每个请求发送preStart条消息。

您还可能希望查看非阻止回调PoisonPillonCompleteonSuccess,而不是使用阻止onFailure。这些回调也尊重超时,并且优于阻止更高的吞吐量。只要上行等待这种响应的方式可以处理异步性(即具有异步能力的Web请求),那么我建议走这条路。

要记住的最后一件事是,即使代码将在超时后返回调用者,如果actor尚未响应,则actor仍然继续处理该消息(执行评估)。它不会因为调用者超时而停止并移动到下一条消息。只是想明确表示不是。

修改

在回应你关于停止长时间执行的评论时,有一些与Akka有关的事情要先考虑。您可以调用停止actor,发送AwaitKill,但如果处理它当前正在处理的消息,则这些都不会停止。它们只是阻止它接收新消息。在你的情况下,使用Rhino,如果有可能进行无限的脚本执行,那么我建议在Rhino中处理这个问题。我会深入研究这篇文章(Stopping the Rhino Engine in middle of execution)的答案,并在演员中设置你的Rhino引擎,如果它已经执行了太长时间它会自动停止。该失败将发送给主管(如果池化)并导致重新启动池化实例,这将在PosionPill中初始化新的Rhino。这可能是处理长时间运行脚本的最佳方法。