阿卡单点故障

时间:2017-01-21 19:27:59

标签: scala akka akka-cluster

我想创建一个不会出现单点故障的系统。 我的印象是路由器是这样做的工具,但我不确定它是否像我期望的那样工作。 这是我的计划的切入点:

object Main extends App{
  val system = ActorSystem("mySys", ConfigFactory.load("application"))
  val router = system.actorOf(
    ClusterRouterPool(RoundRobinPool(0), ClusterRouterPoolSettings(
      totalInstances = 2, maxInstancesPerNode = 1,
      allowLocalRoutees = false, useRole = Some("testActor"))).props(Props[TestActor]),
    name = "testActors")
}

这是运行远程ActorSystem的代码(因此路由器可以将TestActor代码部署到远程节点):

object TestActor extends App{
  val system = ActorSystem("mySys", ConfigFactory.load("application").getConfig("testactor1"))
  case object PrintRouterPath
}

我运行了两次,一次使用testactor1,一次使用testactor2

TestActor代码:

class TestActor extends Actor with ActorLogging{
  implicit val ExecutionContext = context.dispatcher
  context.system.scheduler.schedule(10000 milliseconds, 30000 milliseconds,self, PrintRouterPath)

  override def receive: Receive = {
    case PrintRouterPath =>
     log.info(s"router is on path ${context.parent}")
  }
}

和application.conf

akka{
actor {
  provider = "akka.cluster.ClusterActorRefProvider"
}
remote {
  log-remote-lifecycle-events = off
  netty.tcp {
    hostname = "127.0.0.1"
    port = 2552
  }
}
cluster {
  seed-nodes = [
    "akka.tcp://mySys@127.0.0.1:2552"
    "akka.tcp://mySys@127.0.0.1:2553"
    "akka.tcp://mySys@127.0.0.1:2554"]
  auto-down-unreachable-after = 20s
  }
}
testactor1{
  akka{
    actor {
      provider = "akka.cluster.ClusterActorRefProvider"
    }
    remote {
      log-remote-lifecycle-events = off
      netty.tcp {
        hostname = "127.0.0.1"
        port = 2554
      }
    }
    cluster {
    roles.1 = "testActor"
      seed-nodes = [
        "akka.tcp://mySys@127.0.0.1:2552"
        "akka.tcp://mySys@127.0.0.1:2553"
        "akka.tcp://mySys@127.0.0.1:2554"]
      auto-down-unreachable-after = 20s
    }
  }
}
testactor2{
  akka{
    actor {
      provider = "akka.cluster.ClusterActorRefProvider"
    }
    remote {
      log-remote-lifecycle-events = off
      netty.tcp {
        hostname = "127.0.0.1"
        port = 2553
      }
    }
    cluster {
    roles.1 = "testActor"
      seed-nodes = [
        "akka.tcp://mySys@127.0.0.1:2552"
        "akka.tcp://mySys@127.0.0.1:2553"
        "akka.tcp://mySys@127.0.0.1:2554"]
      auto-down-unreachable-after = 20s
    }
  }
}

现在的问题是,当启动路由器的进程被终止时,运行代码TestActor的actor没有收到任何消息(调度程序发送的消息),我原以为路由器将部署在集群中的另一个种子节点上,并且将恢复actor。这可能吗?或者是否有其他方法来实现此流程并且没有单点故障?

2 个答案:

答案 0 :(得分:2)

我认为,通过仅在一个节点上部署router,您将设置一个主从集群,其中主服务器根据定义是单点故障。

根据我的理解(查看docs),路由器可以是集群感知的,因为它可以在集群中的节点上部署(池模式)或查找(组模式)路由。路由器本身不会通过在集群中的其他位置生成来对故障作出反应。

我相信你有两个选择:

  1. 利用多个路由器使您的系统更具容错能力。路由器可以在路由器之间共享(组模式)或不共享(池模式)。

  2. 使用Cluster Singleton模式 - 允许主从配置,在发生故障时将自动重新生成主服务器。关于您的示例,请注意通过在每个节点中部署actor(ClusterSingletonManager)来实现此行为。这个演员的目的是确定所选的大师是否需要重生并在哪里。对于像您设置的路由器那样的群集感知路由器,这些逻辑都不存在。

  3. 您可以在此Activator sample中找到多个群集设置的示例。

答案 1 :(得分:0)

我测试了两种方法,首先使用代码ClusterRouterPool 就像你说当启动路由器的进程被杀死时,TestActor没有收到更多的消息。 在阅读文档和测试时,如果您更改了application.conf

`auto-down-unreachable-after = 20s` 

这个

`auto-down-unreachable-after = off`

TestActor继续接收消息,但在日志中会出现以下消息(我不知道如何将日志放在这里,抱歉):

[WARN] [01/30/2017 17:20:26.017] [mySys-akka.remote.default-remote-dispatcher-5] [akka.tcp://mySys@127.0.0.1:2554 /system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FmySys%40127.0.0.1%3A2552-0]与远程系统[akka.tcp://mySys@127.0.0.1:2552]的关联失败,地址现在门限为[5000] ms。原因:[关联失败,[akka.tcp://mySys@127.0.0.1:2552]]引起:[连接被拒:/127.0.0.1:2552] [INFO] [01/30/2017 17:20:29.860] [mySys-akka.actor.default-dispatcher-4] [akka.tcp://mySys@127.0.0.1:2554 / remote / akka.tcp / mySys @ 127.0.0.1:2552/user/testActors/c1]路由器在路径上的Actor [akka.tcp://mySys@127.0.0.1:2552 / user / testActors#-1120251475] [WARN] [01/30/2017 17:20:32.016] [mySys-akka.remote.default-remote-dispatcher-5]

在重新启动MainApp的情况下,日志正常工作,没有警告或错误

MainApp日志:

[INFO] [01/30/2017 17:23:32.756] [mySys-akka.actor.default-dispatcher-2] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp://mySys@127.0.0.1:2552] - 欢迎来自[akka.tcp://mySys@127.0.0.1:2554]

TestActor日志:

INFO] [01/30/2017 17:23:21.958] [mySys-akka.actor.default-dispatcher-14] [akka.cluster.Cluster(akka:// mySys)]群集节点[ akka.tcp://mySys@127.0.0.1:2554] - 现有成员的新版本[Member(地址= akka.tcp://mySys@127.0.0.1:2552,status = Up)]正在尝试加入。现有将从群集中删除,然后允许新成员加入。 [INFO] [01/30/2017 17:23:21.959] [mySys-akka.actor.default-dispatcher-14] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp:/ /mySys@127.0.0.1:2554] - 将无法访问的节点[akka.tcp://mySys@127.0.0.1:2552]标记为[Down] [INFO] [01/30/2017 17:23:22.454] [mySys-akka.actor.default-dispatcher-2] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp:/ /mySys@127.0.0.1:2554] - 领导者可以再次履行职责 [INFO] [01/30/2017 17:23:22.461] [mySys-akka.actor.default-dispatcher-2] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp:/ /mySys@127.0.0.1:2554] - 领导者正在删除无法访问的节点[akka.tcp://mySys@127.0.0.1:2552] [INFO] [01/30/2017 17:23:32.728] [mySys-akka.actor.default-dispatcher-4] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp:/ /mySys@127.0.0.1:2554] - 节点[akka.tcp://mySys@127.0.0.1:2552]正在加入,角色[] [INFO] [01/30/2017 17:23:33.457] [mySys-akka.actor.default-dispatcher-14] [akka.cluster.Cluster(akka:// mySys)]群集节点[akka.tcp:/ /mySys@127.0.0.1:2554] - 领导者正在将节点[akka.tcp://mySys@127.0.0.1:2552]移动到[Up] [INFO] [01/30/2017 17:23:37.925] [mySys-akka.actor.default-dispatcher-19] [akka.tcp://mySys@127.0.0.1:2554 / remote / akka.tcp / mySys @ 127.0.0.1:2552/user/testActors/c1]路由器在路径上的Actor [akka.tcp://mySys@127.0.0.1:2552 / user / testActors#-630150507]

另一种方法是使用ClusterRouterGroup,因为路由在群集的节点之间共享

  • 组 - 使用actor选择将消息发送到指定路径的路由器路由可以在群集中不同节点上运行的路由器之间共享。此类路由器的一个用例的一个示例是在群集中的某些后端节点上运行的服务,并由在群集中的前端节点上运行的路由器使用。
  • 池 - 将路由创建为子actor并在远程节点上部署它们的路由器。每个路由器都有自己的routee实例。例如,如果在10节点群集中的3个节点上启动路由器,则如果路由器配置为每个节点使用一个实例,则总共将有30个路由。不同路由器创建的路由不会在路由器之间共享。此类路由器的一个用例的例子是单个主服务器,它协调作业并将实际工作委托给在集群中其他节点上运行的路由。

主要应用程序

object Main extends App {

  val system = ActorSystem("mySys", ConfigFactory.load("application.conf"))
  val routerGroup = system.actorOf(
ClusterRouterGroup(RoundRobinGroup(Nil), ClusterRouterGroupSettings(
  totalInstances = 2, routeesPaths = List("/user/testActor"),
  allowLocalRoutees = false, useRole = Some("testActor"))).props(),
name = "testActors")
}

您必须在每个远程节点中启动TestActor

object TestActor extends App{
  val system = ActorSystem("mySys", ConfigFactory.load("application").getConfig("testactor1"))
  system.actorOf(Props[TestActor],"testActor")
  case object PrintRouterPath
}

http://doc.akka.io/docs/akka/2.4/scala/cluster-usage.html#Router_with_Group_of_Routees

应该在启动actor系统时尽早启动routee actor,因为路由器会在成员状态更改为“Up”后尝试使用它们。

我希望它可以帮到你