akka没有竞争条件的演员选择

时间:2014-12-02 16:49:00

标签: scala akka concurrent.futures

我有一个期货池,每个未来都使用相同的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

使用一个共享全局actor(简单增量计数器)
  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)
    }
  }

但是在并发环境中,由于竞争条件,这种方法不起作用。

我只知道这个问题的一个解决方案 - 在分割到未来之前创建全球演员。但这意味着我无法从顶级库用户那里封装很多隐藏的工作。

2 个答案:

答案 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
    ???
  }
}