2012-05-26 60 views
22

我可以創建actorOf的演員,並使用actorFor查看演員。我現在想要得到一個id:String的演員,如果它不存在,我希望它被創建。事情是這樣的:點播演員獲得或創建

def getRCActor(id: String):ActorRef = { 
    Logger.info("getting actor %s".format(id)) 
    var a = system.actorFor(id) 
    if(a.isTerminated){ 
     Logger.info("actor is terminated, creating new one") 
     return system.actorOf(Props[RC], id:String) 
    }else{ 
     return a 
    } 
    } 

但是,這並不爲isTerminated工作始終是真實的,我得到actor name 1 is not unique!例外第二個電話。我想我在這裏使用了錯誤的模式。有人可以幫助如何實現這一目標?我需要

  • 通過ID按需創建者
  • 查找演員和如果不存在,創建它們
  • 能力摧毀,因爲我不知道我是否會再需要它

我應該使用Dispatcher還是路由器?

解決方案 正如我所建議的,我使用了一個具體的監督者來保存地圖中的可用角色。可以要求提供他的一個孩子。

class RCSupervisor extends Actor { 

    implicit val timeout = Timeout(1 second) 
    var as = Map.empty[String, ActorRef] 

    def getRCActor(id: String) = as get id getOrElse { 
    val c = context actorOf Props[RC] 
    as += id -> c 
    context watch c 
    Logger.info("created actor") 
    c 
    } 

    def receive = { 

    case Find(id) => { 
     sender ! getRCActor(id) 
    } 

    case Terminated(ref) => { 
     Logger.info("actor terminated") 
     as = as filterNot { case (_, v) => v == ref } 
    } 
    } 
} 

他的同伴對象

object RCSupervisor { 

    // this is specific to Playframework (Play's default actor system) 
    var supervisor = Akka.system.actorOf(Props[RCSupervisor]) 

    implicit val timeout = Timeout(1 second) 

    def findA(id: String): ActorRef = { 
    val f = (supervisor ? Find(id)) 
    Await.result(f, timeout.duration).asInstanceOf[ActorRef] 
    } 
    ... 
} 

回答

13

我一直沒有使用阿卡那麼久,但演員的創造者默認情況下,他們的主管。因此,家長可以聽取他們的終止;

var as = Map.empty[String, ActorRef] 
def getRCActor(id: String) = as get id getOrElse { 
    val c = context actorOf Props[RC] 
    as += id -> c 
    context watch c 
    c 
} 

但顯然你需要注意他們的終止;

def receive = { 
    case Terminated(ref) => as = as filterNot { case (_, v) => v == ref } 

這是一個解決方案嗎?我必須說我沒有完全理解你的意思是「終止永遠是真的=>演員姓名1不是唯一的!」

+0

是的,這是一個解決方案。我試圖避免創建我自己的註冊表(地圖),並使用演員路徑爲此。但我計劃無論如何實施一位主管,似乎沒有其他辦法。 一些背景:我正在使用Playframework及其提供的上下文 – martin

+0

感謝您的回答。我從來沒有想到父母會看到Terminated消息。這是完全有道理的,但我被掛在不得不對孩子進行「監視」以處理終止(這使得很少有意義)... – jxstanford

+0

看起來像一個好的解決方案,但我不太喜歡在接近併發的地方使用'var'。 是'mutable.Map'稍微好一點的選項? – Ashesh

12

參與者只能由他們的父母創建,並且從您的描述中我假設您正試圖讓系統創建一個非頂級參與者,這總是會失敗。你應該做的是向父母發送一條信息,說「給我這個孩子」,然後父母可以檢查目前是否存在,身體健康等,可能創建一個新的,然後用適當的結果消息。

要重申這一極其重要的一點:get-or-create只能由直接父級完成。

+0

你說得對。我選擇了oxbow_lakes的答案,因爲他提供了一些代碼示例。在那裏看到我的評論。謝謝 – martin

1

我在oxbow_lakes的代碼/建議基礎上解決了這個問題,但不是創建一個所有兒童演員的簡單集合,而是使用了一個(雙向)映射,如果兒童演員的人數是顯着的。

import play.api._ 
import akka.actor._ 
import scala.collection.mutable.Map 

trait ResponsibleActor[K] extends Actor { 
    val keyActorRefMap: Map[K, ActorRef] = Map[K, ActorRef]() 
    val actorRefKeyMap: Map[ActorRef, K] = Map[ActorRef, K]() 

    def getOrCreateActor(key: K, props: => Props, name: => String): ActorRef = { 
    keyActorRefMap get key match { 
     case Some(ar) => ar 
     case None => { 
     val newRef: ActorRef = context.actorOf(props, name) 
     //newRef shouldn't be present in the map already (if the key is different) 
     actorRefKeyMap get newRef match{ 
      case Some(x) => throw new Exception{} 
      case None => 
     } 
     keyActorRefMap += Tuple2(key, newRef) 
     actorRefKeyMap += Tuple2(newRef, key) 
     newRef 
     } 
    } 
    } 

    def getOrCreateActorSimple(key: K, props: => Props): ActorRef = getOrCreateActor(key, props, key.toString) 

    /** 
    * method analogous to Actor's receive. Any subclasses should implement this method to handle all messages 
    * except for the Terminate(ref) message passed from children 
    */ 
    def responsibleReceive: Receive 

    def receive: Receive = { 
    case Terminated(ref) => { 
     //removing both key and actor ref from both maps 
     val pr: Option[Tuple2[K, ActorRef]] = for{ 
     key <- actorRefKeyMap.get(ref) 
     reref <- keyActorRefMap.get(key) 
     } yield (key, reref) 

     pr match { 
     case None => //error 
     case Some((key, reref)) => { 
      actorRefKeyMap -= ref 
      keyActorRefMap -= key 
     } 
     } 
    } 
    case sth => responsibleReceive(sth) 
    } 
} 

要使用此功能,您從ResponsibleActor繼承,並實現responsibleReceive。注意:此代碼尚未經過徹底測試,可能仍存在一些問題。我忽略了一些錯誤處理以提高可讀性。

0

當前,您可以使用Guka依賴注入與Akka,這在http://www.lightbend.com/activator/template/activator-akka-scala-guice解釋。你必須爲演員創建一個伴隨模塊。在其配置方法中,您需要創建一個到actor類和一些屬性的命名綁定。這些屬性可能來自配置,例如,爲演員配置路由器。您也可以通過編程的方式將路由器配置置於此處。任何你需要引用演員的地方都可以用@Named(「actorname」)注入。配置的路由器將在需要時創建一個actor實例。