on demand actor get or else create

后端 未结 4 1890
甜味超标
甜味超标 2020-12-15 05:04

I can create actors with actorOf and look them with actorFor. I now want to get an actor by some id:String and if it doesnt exist, I w

相关标签:
4条回答
  • 2020-12-15 05:35

    I based my solution to this problem on oxbow_lakes' code/suggestion, but instead of creating a simple collection of all the children actors I used a (bidirectional) map, which might be beneficial if the number of child actors is significant.

    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)
      }
    }
    

    To use this functionality you inherit from ResponsibleActor and implement responsibleReceive. Note: this code isn't yet thoroughly tested and might still have some issues. I ommited some error handling to improve readability.

    0 讨论(0)
  • 2020-12-15 05:39

    Currently you can use Guice dependency injection with Akka, which is explained at http://www.lightbend.com/activator/template/activator-akka-scala-guice. You have to create an accompanying module for the actor. In its configure method you then need to create a named binding to the actor class and some properties. The properties could come from a configuration where, for example, a router is configured for the actor. You can also put the router configuration in there programmatically. Anywhere you need a reference to the actor you inject it with @Named("actorname"). The configured router will create an actor instance when needed.

    0 讨论(0)
  • 2020-12-15 05:44

    I've not been using akka for that long, but the creator of the actors is by default their supervisor. Hence the parent can listen for their termination;

    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
    }
    

    But obviously you need to watch for their Termination;

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

    Is that a solution? I must say I didn't completely understand what you meant by "terminated is always true => actor name 1 is not unique!"

    0 讨论(0)
  • 2020-12-15 05:50

    Actors can only be created by their parent, and from your description I assume that you are trying to have the system create a non-toplevel actor, which will always fail. What you should do is to send a message to the parent saying “give me that child here”, then the parent can check whether that currently exists, is in good health, etc., possibly create a new one and then respond with an appropriate result message.

    To reiterate this extremely important point: get-or-create can ONLY ever be done by the direct parent.

    0 讨论(0)
提交回复
热议问题