我有一个期货池,每个未来都使用相同的akka Actor系统 - 系统中的一些Actors应该是全局的,有些只在一个未来使用。
val longFutures = for (i <- 0 until 2 ) yield Future {
val p:Page = PhantomExecutor(isDebug=true)
Await.result( p.open("http://www.stackoverflow.com/") ,timeout = 10.seconds)
}
PhantomExecutor尝试使用system.actorSelection
def selectActor[T <: Actor : ClassTag](system:ActorSystem,name:String) = {
val timeout = Timeout(0.1 seconds)
val myFutureStuff = system.actorSelection("akka://"+system.name+"/user/"+name)
val aid:ActorIdentity = Await.result(myFutureStuff.ask(Identify(1))(timeout).mapTo[ActorIdentity],
0.1 seconds)
aid.ref match {
case Some(cacher) =>
cacher
case None =>
system.actorOf(Props[T],name)
}
}
但是在并发环境中,由于竞争条件,这种方法不起作用。
我只知道这个问题的一个解决方案 - 在分割到未来之前创建全球演员。但这意味着我无法从顶级库用户那里封装很多隐藏的工作。
答案 0 :(得分:2)
你是对的,确保首先初始化全球参与者是正确的方法。你不能将它们绑定到一个伴侣对象并从那里引用它们,所以你知道它们只会被初始化一次吗?如果你真的不能采用这种方法,那么你可以尝试这样的东西来查找或创建演员。它类似于你的代码,但它包含了一个逻辑,如果竞争条件被命中(仅最多次),则返回查找/创建逻辑(递归):
def findOrCreateActor[T <: Actor : ClassTag](system:ActorSystem, name:String, maxAttempts:Int = 5):ActorRef = {
import system.dispatcher
val timeout = 0.1 seconds
def doFindOrCreate(depth:Int = 0):ActorRef = {
if (depth >= maxAttempts)
throw new RuntimeException(s"Can not create actor with name $name and reached max attempts of $maxAttempts")
val selection = system.actorSelection(s"/user/$name")
val fut = selection.resolveOne(timeout).map(Some(_)).recover{
case ex:ActorNotFound => None
}
val refOpt = Await.result(fut, timeout)
refOpt match {
case Some(ref) => ref
case None => util.Try(system.actorOf(Props[T],name)).getOrElse(doFindOrCreate(depth + 1))
}
}
doFindOrCreate()
}
现在,在创建actor时,重试逻辑将触发任何异常,因此您可能希望进一步指定(可能通过另一个recover
组合器)仅在它获得InvalidActorNameException
时递归,但是你明白了。
答案 1 :(得分:0)
您可能需要考虑创建一个管理员演员,该演员将负责创建&#34; counter&#34;演员。这样就可以确保反演者创建请求被序列化。
object CounterManagerActor {
case class SelectActorRequest(name : String)
case class SelectActorResponse(name : String, actorRef : ActorRef)
}
class CounterManagerActor extends Actor {
def receive = {
case SelectActorRequest(name) => {
sender() ! SelectActorResponse(name, selectActor(name))
}
}
private def selectActor(name : String) = {
// a slightly modified version of the original selectActor() method
???
}
}