用Akka控制外部期货

时间:2013-09-02 19:02:36

标签: multithreading scala threadpool akka future

我一直在使用Akka,而且在更简单的项目中,只使用Scala 2.10期货。然而,有一天我不得不混合一个图书馆返回Futures和Akka Actors,我不知道如何整合Actor调度系统和失控的未来。

在项目中使用Akka actor的选择是能够微调actor队列并控制代码的并行化,并且可能最终向外扩展(即使现在它不是优先级)。但是,如果我有这样的代码(故意简化):

package externallib

object DoSomething {
    def foo(): Future[Something] = ....
}


package myapp

class MyActor extends Actor {
    def receive = {
        case "message" => externallib.DoSomething.foo() pipeTo sender
    }
}

.../
val actorRef = system.actorOf(Props[MyActor].withRouter(
                   RoundRobinRouter(nrOfInstances = 5)))
(0 to 20000) foreach {

    val futureSomething = actorRef ? "message"

}

然后MyActor实际上没有做任何事情,除了在externallib中生成一个线程并直接返回。 externallib future最终会将结果传递给actor的调用者。

然而,通过这种方式,管理actor的良好控制的路由器并不真正控制生成的线程,因为它们是在actor系统之外生成的,即使在相同的ExecutionContext中也是如此。在大循环的示例中,这意味着不是将消息排队到actor,而是将这些消息快速消耗并在任何严格控制的队列之外产生20000个线程。

我在想我可以这样做:

class MyActor extends Actor {
    def receive = {
        case "message" => 
            val res = Await.result(externallib.DoSomething.foo(), someDuration)
            sender ! res
    }
}

这将确保在externallib完成(或超时)之前不会向actor发送新的"message"。但是,这实际上可能需要两个线程(actor的一个和DoSomething中的一个)等待一次计算。

是否有更好的方法来控制在演员系统之外产生的这些未来?

1 个答案:

答案 0 :(得分:0)

MyActor看起来很危险,阻止actor内部并不是一个好主意,事实上,如果ExecutionContext中只有一个线程,那么actor和将来使用它将会死锁。写这个的更好方法是在未来放置一个onComplete,并让演员对一个行为进行become,使其不会发送任何新的未来(可能是stash所有收到的消息)。未来的onComplete既可以向发件人发送消息,也可以通知消息者告诉become原始行为。