Akka ClusterSingletonProxy到远程部署的单例

时间:2017-06-27 11:34:25

标签: scala akka akka-cluster

我正在尝试向通过另一个演员部署在远程节点上的单身演员发送消息。

这是等待memberUp事件的管理器,然后在该节点上部署Worker actor,然后向该单例发送一条消息:

object Manager extends App {
  val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
  sys.actorOf(Props[Manager], "manager")
}

class Manager extends Actor with ActorLogging {
  override def receive: Receive = {
    case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
      context.system.actorOf(ClusterSingletonManager.props(
        singletonProps = Props(classOf[Worker]),
        singletonName = "worker",
        terminationMessage = End,
        role = Some("worker")).withDeploy(Deploy(scope = RemoteScope(member.address))))

      context.actorOf(ClusterSingletonProxy.props(
        singletonPath = s"/user/singleton/worker",
        role = Some(s"worker")), "worker") ! "hello"
  }
  override def preStart(): Unit = {
    Cluster(context.system).subscribe(self,classOf[MemberUp])
  }
}

这是工人:

object Worker extends App{
  ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))
}

class Worker extends Actor with ActorLogging {

  override def receive: Receive = {
    case msg =>
      println(s"GOT MSG : $msg from : ${sender().path.name}")
  }
}

和application.conf:

manager {
  akka {
    actor {
      provider = "akka.cluster.ClusterActorRefProvider"
    }
    cluster {
      auto-down-unreachable-after = 20s
      seed-nodes = [
        "akka.tcp://mySys@127.0.0.1:2552"
      ]
      roles.1 = "manager"

    }
    remote.netty.tcp.port = 2552

  }
}

worker {
  akka {
    cluster {
      auto-down-unreachable-after = 20s
      seed-nodes = [
        "akka.tcp://mySys@127.0.0.1:2552"
      ]
      roles.1 = "worker"
    }
    remote.netty.tcp.port = 2554
    actor {
      provider = "akka.cluster.ClusterActorRefProvider"
    }
  }
}

工作程序已初始化(我可以在日志中看到state change [Start -> Oldest]消息)但是从管理器发送的消息永远不会到达工作人员。当我在远程节点上部署单例时,它曾经工作正常,但现在我希望管理器部署它。

我还尝试将其部署为管理器的子项(使用上下文而不是context.system)并将单例路径更改为user/manager/singleton/worker,但它不起作用。

我正在使用Akka 2.3.11

编辑: sbt文件:

name := "MyProject"
version := "1.0"
scalaVersion := "2.10.5"
libraryDependencies +=
    "com.typesafe.akka" %% "akka-actor" % "2.3.11",
    "com.typesafe.akka" %% "akka-cluster" % "2.3.11",
    "joda-time" % "joda-time" % "2.0",
    "com.typesafe.akka" %% "akka-contrib" % "2.3.11"

1 个答案:

答案 0 :(得分:2)

所以我在创建ClusterSingletonManager的不同选项中玩了一下,我认为远程部署它们会破坏单例模式中的某些内容。我收集了一些指标:

  • 由于它是远程部署,因此工作节点上ClusterSingletonManager的路径为/remote/akka.tcp/mySys@127.0.0.1:2552/user/worker。我不认为库可以/将处理这个,因为它期望/user/worker

  • 尝试使用ClusterSingletonProxy登录主节点从DEBUG模式发送消息时No singleton available, stashing message hello workerTrying to identify singleton at akka.tcp://mySys@127.0.0.1:2552/user/worker/singleton(失败并重试) - >它正在寻找错误节点上的单例,因为没有经理可用,而且显然不知道单例是在工作节点上。

在工作节点上直接创建ClusterSingletonManager时,一切都按预期工作。

您的经理命名也存在问题。您的singletonNameworker,而您的经理本身(演员)没有任何名字。创建代理时,使用路径/user/singleton/worker,但路径应如下所示:/user/{actorName}/{singletonName}。因此,在我的代码中,我使用worker作为actorName,singleton作为singletonName

所以这是我的工作代码:

object Manager extends App {
  val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
  sys.actorOf(Props[Manager], "manager")
}

class Manager extends Actor with ActorLogging {
  override def receive: Receive = {
    case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
      context.actorOf(ClusterSingletonProxy.props(
        singletonPath = s"/user/worker/singleton",
        role = Some("worker")), name = "workerProxy") ! "hello worker"
  }
  override def preStart(): Unit = {
    Cluster(context.system).subscribe(self,classOf[MemberUp])
  }
}
object Worker extends App{
  val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))

  sys.actorOf(ClusterSingletonManager.props(
    singletonProps = Props(classOf[Worker]),
    singletonName = "singleton",
    terminationMessage = PoisonPill,
    role = Some("worker")), name = "worker")
}

class Worker extends Actor with ActorLogging {

  override def receive: Receive = {
    case msg =>
      println(s"GOT MSG : $msg from : ${sender().path.name}")
  }
}

application.conf和build.sbt保持不变。

编辑

通过引用ClusterSingletonProxy与工作节点上的实际路径(计算它是网络路径)来使用它。我不确定我是否会推荐这个,因为我仍然不确定,如果该库设计为能够做到这一点,但它至少在这个最小的例子中起作用:

object Manager extends App {
  val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
  sys.actorOf(Props[Manager], "manager")
}

class Manager extends Actor with ActorLogging {
  override def receive: Receive = {
    case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
      val ref = context.system.actorOf(ClusterSingletonManager.props(
        singletonProps = Props(classOf[Worker]),
        singletonName = "singleton",
        terminationMessage = PoisonPill,
        role = Some("worker")).withDeploy(Deploy(scope = RemoteScope(member.address))), name = "worker")

      context.actorOf(ClusterSingletonProxy.props(
        singletonPath = s"${ref.path.toStringWithoutAddress}/singleton", // /remote/akka.tcp/mySys@127.0.0.1:2552/user/worker/singleton
        role = Some("worker")), name = "workerProxy") ! "hello worker"
  }
  override def preStart(): Unit = {
    Cluster(context.system).subscribe(self,classOf[MemberUp])
  }
}
object Worker extends App{
  val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))
}

class Worker extends Actor with ActorLogging {

  override def receive: Receive = {
    case msg =>
      println(s"GOT MSG : $msg from : ${sender().path.name}")
  }
}