动态加载一个Actor,如果它在那里

时间:2015-02-23 05:05:43

标签: scala akka scala-reflect

我有以下代码

lazy val restEndpoint = context.actorOf(Props[RestEndpoint], "RestEndpoint")

但是,我想动态加载actor,如果它存在的原因有以下几点:

  1. 它可能不在类路径上,所以我不得不问类加载器它是否在那里。
  2. 即使它在类路径上,我也可能不想因配置原​​因加载它。
  3. RestEndpoint位于不同的JAR文件中,该文件已经依赖于此JAR文件,因此我不能拥有循环依赖项。
  4. 有一种'简单'的方法可以反思地做到这一点吗?请不要指出Scala中有关反射的文档,因为那里没有什么容易的。如果有一个Scala Reflection for Dummies讨论,我会很感激地看着它。

    非常感谢一个工作范例。

3 个答案:

答案 0 :(得分:5)

反映对classpath以外的事物没有帮助。任何不在类路径中的东西都意味着"Java does not know about the existence of this entity."

现在,当涉及到任何实例化的actor时(无论在哪里......甚至可以在所有Akka关心的替代宇宙中......),你可以通过{{1来引用它们通过使用他们的ActorRef地址(路径,嗯......替代宇宙......嗯......简单,问你的替代自我:p)示例 -

fully qualified

如果val as = ActorSystem( "myActorSystem" ) val refToRemoteActor: ActorSelection = as.actorSelection( "akka.tcp://my-sys@host.example.com:5678/user/service-b" ) // Now You can tell anything to your ActorSelction. But you can not ask them. refToRemoteActor ! "my-message" 需要ActorRef,则需要向演员发送消息,例如内置的remote Actor消息,并使用Identify参考sender()的回复。

注意:如果有人在为alternate-universe获取Actors时遇到问题,remote Actor还没有提供Alternate-universe功能。但是,你可以实现它,就好像你可以通过那个量子隧道获得LAN电缆......或者不管它到底是什么"。

答案 1 :(得分:0)

我会尝试找到另一种处理循环依赖问题的方法,因为通过反射实例化actor会让你遇到麻烦,但是有可能如下面的代码所示:

package code

import akka.actor._
import scala.util._

object Test extends App{

  val system = ActorSystem("test")

  val shouldSucceed = tryAndInstantiate("code.MyTestActor")
  println(shouldSucceed)

  val shouldFail = tryAndInstantiate("code.FooTestActor")
  println(shouldFail )

  def tryAndInstantiate(name:String):Try[ActorRef] = {
    Try{
      val clazz = Class.forName(name)
      system.actorOf(Props(clazz))
    }
  }

}

class MyTestActor extends Actor{
  def receive = {
    case _ =>
  }
}

如果类路径中不存在该类,则会出现Failure。您还可以向tryAndInstantiate添加其他逻辑,以满足您检查配置的需要。

答案 2 :(得分:0)

非常高兴我们有Typesafe支持合同。这是我们提出的解决方案。我测试了代码,它的工作原理。注意:没有必要反思,这让我很开心。

def actorRefForName(className: String) = try {
  val actorClass = Class.forName(className)
  Some(context.actorOf(Props(actorClass), actorClass.getSimpleName))
} catch {
  case classNotFoundException: ClassNotFoundException =>
  log.info(s"class $className not found. This actor will not be used")
  None
}

. . .

lazy val kinesisProducer =
  actorRefForName("com._3tierlogic.KinesisManager.producer.KinesisProducer")

. . .

def receive = {

  case Start =>

    kinesisProducer match {
      case Some(kinesisProducerRef) =>
        log.info("Starting " + kinesisProducerRef.path.name)
        kinesisProducerRef ! Start
      case None =>
        log.info("There is no Kinesis Producer actor to start.")
    }

  case Started =>

    // foreach is a little confusing when there is only Some or None, but
    // basically we can only use our actorRef if there is one. EK
    kinesisProducer.foreach { kinesisProducerRef =>
      if (sender.equals(kinesisProducerRef)) {
        log.info(kinesisProducerRef.path.name + " Started")
        log.info("Starting " + restEndpointRef.path.name)
        IO(Http)(context.system) ! Http.Bind(restEndpointRef, "0.0.0.0", port = 8061)
      }
}

虽然这增加了一些额外的样板,但我认为这不太糟糕。我可能有办法更多地减少样板。

Typesafe还建议我查看Akka ExtensionsExtendedActorSystem