我可以使用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!
异常。我想我在这里使用了错误的模式。有人可以帮助如何实现这一目标吗?我需要
我应该使用Dispatcher或Router吗?
解决方案 如我所建议的那样,我使用一个具体的Supervisor来保存地图中可用的actor。可以要求他提供一个孩子。
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]
}
...
}
答案 0 :(得分:14)
我没有长时间使用akka,但是演员的创建者默认是他们的主管。因此,父母可以听取他们的终止;
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不是唯一的!”
答案 1 :(得分:13)
演员只能由他们的父级创建,并且从您的描述中我假设您正在尝试让系统创建一个非顶级演员,这将永远失败。你应该做的是向家长发送一条消息“在这里给我这个孩子”,然后父母可以检查当前是否存在,健康状况良好等,可能创建一个新的,然后用适当的方式回复结果消息。
重申这个非常重要的观点:get-or-create只能由直接父母完成。
答案 2 :(得分:2)
我在oxbow_lakes的代码/建议上基于我的解决方案来解决这个问题,但是我没有使用(双向)映射创建所有子actor的简单集合,如果子actor的数量很大,这可能是有益的。
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
。注意:此代码尚未经过全面测试,可能仍存在一些问题。我省略了一些错误处理以提高可读性。
答案 3 :(得分:0)
目前,你可以在Akka上使用Guice依赖注入,这在http://www.lightbend.com/activator/template/activator-akka-scala-guice进行了解释。您必须为actor创建一个附带的模块。在configure方法中,您需要创建一个与actor类和一些属性的命名绑定。属性可以来自配置,例如,为actor配置路由器。您也可以通过编程方式将路由器配置放在那里。在任何地方你都需要引用你用@Named注入的actor(&#34; actorname&#34;)。配置的路由器将在需要时创建一个actor实例。