2016-06-11 4 views
2

Я хотел бы иметь возможность создавать несколько экземпляров одного и того же родительского актера, но с разными дочерними актерами. Я предполагаю, что это должно быть возможно с Guice, но я не нашел решения.Scala/Akka/Guice динамически впрыскивающие дочерние актеры

Вот что я имею в виду ~

Контроллер:

class Application @Inject()(@Named(ParentActor.parentActor1) parentActor1: ActorRef, 
         @Named(ParentActor.parentActor2) parentActor2: ActorRef) 
    extends Controller { 

    def index = Action { 
    parentActor1 ! "Message" 
    parentActor2 ! "Message" 
    Ok() 
    } 
} 

Родитель Актер:

object ParentActor { 
    final val parentActor1 = "parentActor1" 
    final val parentActor2 = "parentActor2" 
} 

class ParentActor @Inject() (childActor: ActorRef) extends Actor { 

    def receive = { 
    case "Message" => 
     println(s"ParentActor ${self.path} received message...") 
     childActor ! "Message" 
    } 
} 

Ребенок Актер A:

class ChildActorA extends Actor { 

    def receive = { 
    case "Message" => 
     println("ChildActorA received message...") 
    } 
} 

Детский Актер B:

class ChildActorB extends Actor { 

    def receive = { 
    case "Message" => 
     println("ChildActorB received message...") 
    } 
} 

Модуль:

class Modules extends AbstractModule with AkkaGuiceSupport { 

    override def configure() = { 
    bindActor[ParentActor](ParentActor.parentActor1) 
    bindActor[ParentActor](ParentActor.parentActor2) 
    } 
} 

Что делать, если я хотел "parentActor1", чтобы иметь "childActor" точку реф к экземпляру ChildActorA и "parentActor2", чтобы ссылка refentAndler указала на экземпляр ChildActorB? Можно ли это достичь с помощью Guice?

+0

Вы пытались внедрить 'Props' вместо' ActorRef'? – Sergey

+0

@ Сэрджи вы могли бы немного разобраться? – Nick

+0

Я предполагаю, что одним из альтернативных решений будет использование того, что, как мне кажется, называется «введение», посредством которого ParentActor будет «введен» соответствующему ребенку-актеру. Например, мой контроллер отправит сообщение в ParentActor, например «Message (childActor)», где «childActor» является ActorRef соответствующему дочернему игроку (либо ChildActorA, либо ChildActorB). – Nick

ответ

3

Я использую код, основанный на https://github.com/rocketraman/activator-akka-scala-guice, чтобы сделать что-то подобное

Я не использую Play, так что я должен инициализировать Guice и грузиться актер

import akka.actor._ 
import javax.inject.{Inject, Provider, Singleton} 
import com.google.inject.AbstractModule 
import net.codingwell.scalaguice.InjectorExtensions._ 
import com.google.inject.Guice 
import com.google.inject.Injector 
import scala.concurrent.Await 
import scala.concurrent.duration.Duration 

object Bootstrap extends App { 

    val injector = Guice.createInjector(
    new AkkaModule(), 
    new ServiceModule() 
) 

    implicit val system = injector.instance[ActorSystem] 

    val parentActor1 = system.actorOf(ParentActor.props(ChildActorA.name)) 
    val parentActor2 = system.actorOf(ParentActor.props(ChildActorB.name)) 


    parentActor1 ! "Message" 
    parentActor2 ! "Message" 

    system.terminate() 

    Await.result(system.whenTerminated, Duration.Inf) 
} 

Для инициализации Guice есть два классы/объекты:

Один инициализировать расширение и инъекционные системы актера, где это требуется

import akka.actor.ActorSystem 
import AkkaModule.ActorSystemProvider 
import com.google.inject.{AbstractModule, Injector, Provider} 
import com.typesafe.config.Config 
import net.codingwell.scalaguice.ScalaModule 
import javax.inject.Inject 

object AkkaModule { 

    class ActorSystemProvider @Inject() (val injector: Injector) extends Provider[ActorSystem] { 

    override def get() = { 

     val system = ActorSystem("actor-system") 

     GuiceAkkaExtension(system).initialize(injector) 

     system 
    } 
    } 
} 

class AkkaModule extends AbstractModule with ScalaModule { 

    override def configure() { 
    bind[ActorSystem].toProvider[ActorSystemProvider].asEagerSingleton() 
    } 

} 

еще один, чтобы создать поставщиков для детей

import javax.inject.Inject 

import akka.actor.{Actor, ActorRef, ActorSystem} 
import com.google.inject.name.{Named, Names} 
import com.google.inject.{AbstractModule, Provides, Singleton} 
import net.codingwell.scalaguice.ScalaModule 

class ServiceModule extends AbstractModule with ScalaModule with GuiceAkkaActorRefProvider { 

    override def configure() { 
    bind[Actor].annotatedWith(Names.named(ChildActorA.name)).to[ChildActorA] 
    bind[Actor].annotatedWith(Names.named(ChildActorB.name)).to[ChildActorB] 
    } 

    @Provides 
    @Named(ChildActorA.name) 
    def provideChildActorARef(@Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorA.name) 

    @Provides 
    @Named(ChildActorB.name) 
    def provideChildActorBRef(@Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorB.name) 

} 

Расширение

import akka.actor._ 
import com.google.inject.Injector 

class GuiceAkkaExtensionImpl extends Extension { 

    private var injector: Injector = _ 

    def initialize(injector: Injector) { 
    this.injector = injector 
    } 

    def props(actorName: String) = Props(classOf[GuiceActorProducer], injector, actorName) 

} 

object GuiceAkkaExtension extends ExtensionId[GuiceAkkaExtensionImpl] with ExtensionIdProvider { 

    override def lookup() = GuiceAkkaExtension 

    override def createExtension(system: ExtendedActorSystem) = new GuiceAkkaExtensionImpl 

    override def get(system: ActorSystem): GuiceAkkaExtensionImpl = super.get(system) 

} 

trait NamedActor { 
    def name: String 
} 

trait GuiceAkkaActorRefProvider { 

    def propsFor(system: ActorSystem, name: String) = GuiceAkkaExtension(system).props(name) 

    def provideActorRef(system: ActorSystem, name: String): ActorRef = system.actorOf(propsFor(system, name)) 

} 

производитель

import akka.actor.{IndirectActorProducer, Actor} 
import com.google.inject.name.Names 
import com.google.inject.{Key, Injector} 

class GuiceActorProducer(val injector: Injector, val actorName: String) extends IndirectActorProducer { 

    override def actorClass = classOf[Actor] 

    override def produce() = injector.getBinding(Key.get(classOf[Actor], Names.named(actorName))).getProvider.get() 

} 

и ваших актеров

import javax.inject.Inject 
import akka.actor._ 


object ParentActor { 

    def props(childName: String)(implicit @Inject() system: ActorSystem) = Props(classOf[ParentActor],system.actorOf(GuiceAkkaExtension(system).props(childName))) 

} 

class ParentActor (childActor: ActorRef) extends Actor { 

    def receive = { 
    case "Message" => 
     println(s"ParentActor ${self.path} received message...") 
     childActor ! "Message" 
    } 
} 

object ChildActorA extends NamedActor{ 

    override final val name = "ChildActorA" 
    def props() = Props(classOf[ChildActorA]) 

} 

class ChildActorA extends Actor { 

    def receive = { 
    case "Message" => 
     println("ChildActorA received message...") 
    } 
} 

object ChildActorB extends NamedActor{ 

    override final val name = "ChildActorB" 
    def props() = Props(classOf[ChildActorB]) 

} 


class ChildActorB extends Actor { 

    def receive = { 
    case "Message" => 
     println("ChildActorB received message...") 
    } 
} 

выход из SBT

> run 
[info] Running Bootstrap 
ParentActor akka://actor-system/user/$b received message... 
ParentActor akka://actor-system/user/$d received message... 
ChildActorB received message... 
ChildActorA received message... 
[success] Total time: 1 s, completed Jun 14, 2016 1:23:59 AM 

Вы должны явно назвать детей,

Это не самый чистый и самый изящный ответ, и я уверен, что код может быть оптимизирован, но он позволяет создавать экземпляры одного и того же родителя с разными детьми.

Я имею в виду, что вы также можете использовать BindingAnnotations

+0

Спасибо за подробный ответ! Позвольте мне проверить это, когда я получу свободный момент, и я вернусь к вам ... – Nick

+0

Конечно! Сообщите мне, если бы это сработало для вас – motilio

Смежные вопросы