Akka-cluster 在 EC2/docker 上持有并继续存储有关成员资格的错误信息

Akka-cluster holds and keeps storing wrong info about membership on EC2/docker

环境:scala-2.11.xakka-2.5.9

EC2 上有两个主机:H1H2。 sbt-project 的树模块有:masterclientworker。 每个模块都实现 akka-cluster 节点,它订阅集群事件并记录它们。此外,每个节点每 1 分钟记录一次集群状态(用于调试)。以下端口用于集群节点:master: 2551worker: 3000client: 5000

项目可用 github

有关基础设施的更多详细信息:

模块可以在 H1H2 中随机重新部署。

akka 集群有一个奇怪的行为。重新部署其中一个节点(例如 worker)时。以下步骤说明了部署历史:

初始状态 - 当 worker 部署在 H1 并且 masterclient 部署在 H2

----[state-of-deploying-0]---  
H1 = [worker]
H2 = [master, client]

cluster status:    // cluster works correctly
  Member(address = akka.tcp://ClusterSystem@H1:3000, status = Up)
  Member(address = akka.tcp://ClusterSystem@H2:2551, status = Up)
  Member(address = akka.tcp://ClusterSystem@H2:5000, status = Up)
----------------

之后 worker 模块已在主机 H2

上重新部署
----[state-of-deploying-1]---  
H1 = [-]
H2 = [master, client, worker (Redeployed)]

cluster status:    // WRONG cluster state!
  Member(address = akka.tcp://ClusterSystem@H1:3000, status = Up) // ???
  Member(address = akka.tcp://ClusterSystem@H2:2551, status = Up)
  Member(address = akka.tcp://ClusterSystem@H2:3000, status = WeaklyUp)
  Member(address = akka.tcp://ClusterSystem@H2:5000, status = Up)
----------------

以上情况偶有发生。在这种情况下,集群存储错误的成员状态并且不会修复它:

Member(address = akka.tcp://ClusterSystem@H1:3000, status = Up) // ???

主机 H1 不包含 worker 的任何实例。并且 > telnet H1 3000 returns connection refused。 但是为什么 akka-cluster 一直存储这个错误的信息?

此行为是有意为之,生产中的 Akka 集群应该 运行 没有自动关闭,以防止 split-brain 问题。

想象一个具有两个客户端(X 和 Y)的双节点(A 和 B)集群:

  • 一开始一切正常, Y 连接到 B 的请求可以转发到 Actor1 运行ning 在 A for通过远程处理
  • 然后,由于网络分区,A 无法从 B 访问,B 可能会试图将 A 标记为关闭并重新启动 Actor1 本地。
  • 客户端 YB 运行ning

  • 上向 Actor1 运行 发送消息
  • 网络分区另一端的客户端X仍然连接到A,并将向Actor1

    [=48发送消息=]

这是一个 split-brain 问题:具有相同标识符的同一个 actor 运行ning 在具有两种不同状态的两个节点上。重建正确的 actor 状态非常困难或不可能。

为防止这种情况发生,您必须针对您的问题或用例选择合理的停机策略:

  • 如果你负担得起,就用手动downing吧。操作员将识别出集群确实已关闭,并将节点标记为不可访问。
  • 如果您的集群具有动态数量的节点,那么您需要一些复杂的东西,例如 Lightbend Split Brain Resolver
  • 如果你的集群是静态的,你可以使用仲裁策略来避免脑裂。您总是需要 运行 奇数个节点。