动态加载 Actor(如果存在)

Dynamically Load an Actor if it's there

我有以下代码

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

但是,如果 actor 存在,我想动态加载它,原因如下:

  1. 它可能不在 class 路径上,所以我必须询问 class 加载程序是否存在。
  2. 即使它在 class 路径上,出于配置原因我可能不想加载它。
  3. RestEndpoint 位于另一个 JAR 文件中,该 JAR 文件已经依赖于此 JAR 文件,所以我不能有循环依赖。

是否有一些 'easy' 方法可以反思性地做到这一点?请不要指点我关于 Scala 中反射的文档,因为那里没有什么容易的。如果有 Scala Reflection for Dummies 讨论,我将不胜感激。

将不胜感激。

对于不在您 classpath 中的内容,反思无济于事。不在 class 路径中的任何内容都表示 "Java does not know about the existence of this entity.".

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

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"

如果您的 remote Actor 需要 ActorRef,您需要向演员发送消息,例如内置的 Identify 消息,并使用 sender() 参考 remote Actor.

的回复

注意:如果有人在获取替代宇宙的 Actors 时遇到问题,Akka 尚未提供替代宇宙功能。但是,只要您可以通过 "quantum tunnel... or whatever the hell it is".

获得 LAN 电缆,您就可以继续使用它

我会尝试找到另一种方法来处理您的循环依赖问题,因为通过反射实例化 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 _ =>
  }
}

如果 class 在 class 路径上不存在,您将得到一个失败。您还可以向 tryAndInstantiate 添加额外的逻辑以满足您检查配置的需要。

很高兴我们有 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 Extensions and ExtendedActorSystem