使用 Akka Cluster 的服务比无状态应用程序有额外的要求。为了形成集群,每个 Pod 需要知道哪些其他 Pod 已部署为该服务的一部分,以便它们可以相互连接。 Akka 提供了一个 Cluster Bootstrap 库,允许 Kubernetes 中的 Akka 应用程序使用 Kubernetes API 自动发现这一点。流程大致如下:
当应用程序启动时,应用程序会轮询 Kubernetes API 以查找部署了哪些 Pod,直到发现配置的最小数量的 Pod。
然后,它尝试使用 Akka 的 HTTP 管理接口连接到这些 pod,并查询这些 pod 是否已经形成集群。
如果集群已经形成,则应用程序将加入该集群。
如果任何 pod 上尚未形成集群,则使用确定性函数来决定哪个 pod 将启动集群 - 该函数可确保当前正在经历此过程的所有 pod 都会决定同一个 pod。
决定启动集群的 pod 与自身形成一个集群。
剩余的 Pod 轮询该 Pod,直到它报告它已形成一个集群,然后它们加入该集群。
有关此过程的更详细说明,请参阅Akka Cluster Bootstrap 文档。
下面是引导过程的更详细描述。下面的所有配置属性参考均位于 akka.management.cluster.bootstrap
部分中。
每个节点使用 Akka Discovery 发现其“邻居”
节点开始探测已发现节点的联系点(这些节点是 HTTP 端点,由 Bootstrap 管理扩展通过 Akka 管理公开)以查找要加入的已知种子。
当尚不存在集群时,在探测过程中,所联系的节点都不会返回任何种子节点。创建新集群时将执行以下操作:
服务每隔contact-point-discovery.interval
的时间发现节点。
如果发现返回相同的联系点contact-point-discovery.stable-margin
。这是为了防止根据不断变化的联系点做出加入决策。
至少需要发现contact-point-discovery.required-contact-point-nr
个节点。
所有发现的接触点的已互相通过HTTP通信且 请求-响应成功。
每个节点都会通过互相探测,若没有得到seed-nodes
的任何信息时,说明不存在集群,应该形成一个新的集群,它们知道自己的所有地址,并决定“最低的” 地址” 排序后的地址开始形成集群。
根据 IP 地址排序后的第一个地址节点(例如“A”)也注意到了这一点,并决定_加入自己_ 。
一旦地址最低的节点(A)加入自身,它就形成了一个新的集群。
其他节点注意到 A 已开始返回_自身_作为联络点响应中的种子节点。
任何节点,包括X,都会立即加入它在接触点过程中观察到的种子节点。
节点继续探测其他节点,最终会注意到属于集群的任何现有节点,并立即加入它。
最终所有节点都加入到同一个集群中,整个过程就完成了。
含义:
contact-point-discovery.interval
:Akka节点之间定期相互检查和更新它们的已知联系点(contact points)的时间间隔。akka.cluster.typed.contact-point-discovery.stable-margin
:在新节点加入集群之前,其他节点必须在多长时间内保持稳定状态。用于确保集群中的所有节点都能够稳定地加入和连接。重要的是要实现不 动态和自动 集群加入解决方案提供 100% 的安全性,但是这里介绍的过程非常接近它。请注意,经常使用的对种子节点使用一致数据存储的声明也不是 100% 安全(原文如此!),因为从强一致存储中发现节点并尝试加入操作的节点之间可能会发生竞争.
Akka bootstrap 的解决方案很容易出现很少且相当罕见的竞争。针对竞争情况的内置保护以稳定超时的形式存在,这意味着如果在发现过程中观察到任何变化,决策将被延迟,直到观察再次稳定。这可以防止在发现仍然不一致时启动加入。
另请注意,在向现有集群添加新节点时,引导过程并不依赖于发现机制的完全一致性。这是非常理想的,因为这种情况通常发生在由于服务负载增加而动态扩展时,而某些服务确实可能不完全一致。然而,Akka 集群成员协议是高度一致的,并且它是集群组成的真实来源,并且没有外部系统可以拥有关于此的更可靠的信息(因为它可能已经过时)。这就是为什么存在接触点探测机制,即使发现仅返回_部分_甚至_不同的一组每次查找的节点_ 由于 Akka Cluster 的成员身份和八卦协议的工作方式,探测仍允许节点加入所有正确的节点。总而言之,引导机制对于向系统添加节点非常有效,即使在负载下,即使 DNS 系统不完全一致。
然而,如果我们讨论的是初始引导期间 DNS 查找响应不一致,则节点将延迟形成集群,因为它们期望查找一致,这是通过 stable-margin 配置选项检查的。
笔记
对于节点在初始引导过程中对其邻居拥有一致的视图至关重要。否则多个节点可以自加入并启动多个集群。
上面的部分解释了默认的JoinDecider
实现。可以用配置属性 join-decider.class
替换实现。请参阅reference.conf
和 API 文档。