本章介绍如何在多个数据中心,可用性区域、区域中使用Akka群集。
使Akka集群了解数据中心边界的原因是,与同一数据中心中的节点之间的通信相比,跨数据中心的通信通常具有更高的延迟和更高的故障率。
但是,节点的分组不限于数据中心的物理边界,即使这是主要用例。由于其他原因,它也可以用作逻辑分组,例如隔离某些节点以提高稳定性,或将大型集群分成较小的节点组以实现更好的可伸缩性。
依赖
要使用Akka Cluster,请在您的项目中添加以下依赖项:
动机
使用多个中心的原因可能很多,例如:
- 冗余,容忍一个位置的故障,并且仍然可以运行。
- 服务来自用户附近位置的请求,以提供更好的响应能力。
- 平衡许多服务器上的负载。
可以使用默认设置来运行普通的Akka群集,该默认设置跨越多个数据中心,但可能导致以下问题:
- 群集成员关系的管理在网络分区期间被暂停,如下面单独的部分所述。这意味着在数据中心之间的网络分区期间,无法添加和删除节点。
- 跨数据中心网络连接的误报故障检测更加频繁。数据中心内和不同数据中心之间无法针对故障检测进行不同的设置。
- 在网络分区的情况下,关闭/删除节点通常应针对数据中心内或跨数据中心的故障进行不同的处理。对于数据中心之间的网络分区,系统通常不应关闭无法访问的节点,而应等待其修复或由人工或外部监控系统做出决定。对于同一数据中心内的故障,可以采用自动的,更激进的宕机机制进行快速故障转移。
- 很难以安全的方式将Cluster Singleton和Cluster Sharding从一个数据中心快速故障转移到另一个数据中心。存在单例或分片实体在网络分区的两侧都处于活动状态的风险。
- 位置信息的缺乏使得,难以优化选择更近的节点,而不是较远的节点。例如。如果群集感知路由器选择将消息路由到自己数据中心的节点,则效率会更高。
为了避免其中一些问题,每个数据中心可以运行一个单独的Akka群集,并在数据中心之间使用另一个通信通道,例如HTTP,即一个外部消息代理。但是,基于集群成员关系信息构建的许多不错的工具都丢失了。例如,不可能在各个群集之间使用分布式数据。
我们通常建议将微服务实现为一个Akka集群。该服务的外部API是HTTP,gRPC或消息代理,而不是Akka Remoting或Cluster(请参阅When and where to use Akka Cluster中的附加讨论)
在多个节点上运行的服务的内部通信将使用普通的actor消息传递或基于Akka Cluster的工具。当将此服务部署到多个数据中心时,如果内部通信使用了多个Akka群集而无法使用普通的Actor消息传递,那么将很不方便。在内部使用Akka消息传递的好处是性能,开发的便利性以及Actor的领域推理能力。
因此,可以使Akka集群了解数据中心,以便一个Akka集群可以跨越多个数据中心,并且仍然可以容忍网络分区。
定义数据中心
这些功能基于以下想法:可以通过设置akka.cluster.multi-data-center.self-data-center属性将节点分配给一组节点。一个节点只能属于一个数据中心,如果未指定任何内容,则一个节点将属于默认数据中心。
节点的分组不限于数据中心的物理边界,即使那是主要用例。由于其他原因,它也可以用作逻辑分组,例如隔离某些节点以提高稳定性,或将大型群集分成较小的节点组以实现更好的可伸缩性。
成员关系
一些成员关系变化由一个称为领导者的节点管理。每个数据中心只有一位领导者,并且负责同一数据中心内成员的这些变化。其他数据中心的成员由相应数据中心的负责人独立管理。在数据中心的节点之间发现不可达性时,无法执行这些操作,但是不同数据中心之间的不可达性不会影响数据中心内成员关系管理的进度。当数据中心之间存在网络分区时,也可以添加和删除节点,如果未将节点分组到数据中心中,则无法添加节点。
可以将诸如加入,离开和关闭之类的用户操作发送到群集中的任何节点,而不仅发送到该节点的数据中心中的节点。种子节点也是全局的。
通过将前缀为“ dc-”的数据中心名称添加到成员的角色来实现数据中心成员关系,集群中所有其他成员都知道此信息。这是一个实现细节,但是最好在日志消息中看到它。
您可以检索有关成员所属的数据中心的信息:
故障检测
通过发送心跳消息来执行故障检测,以检测节点是否不可访问。与在整个数据中心中相比,在同一数据中心中的节点之间更频繁,更确定地执行此操作。跨不同数据中心的故障检测应解释为数据中心之间网络链接出现问题的指示。
可以为这两个目的配置两个不同的故障检测器:
- akka.cluster.failure-detector用于在自己的数据中心内进行故障检测
- akka.cluster.multi-data-center.failure-detector用于跨不同数据中心的故障检测
订阅群集事件时,UnreachableMember和ReachableMember事件用于在自己的数据中心内进行观察。与订阅注册所在的数据中心相同。
对于跨数据中心不可达性通知,您可以订阅UnreachableDataCenter和ReachableDataCenter事件。
用于跨数据中心进行故障检测的心跳消息仅在每侧的多个最旧节点之间执行。节点数由akka.cluster.multi-data-center.cross-data-center-connections配置。仅使用有限数量的节点的原因是为了使数据中心之间的连接数保持较低。跨数据中心分发成员关系信息时,相同的节点也用于gossip协议。在数据中心内,所有节点都涉及gossip和故障检测。
这影响了滚动升级的方式。不要同时停止所有在gossip的最老节点。一次停止一个或几个,以便新节点可以接管责任。最好将最早的节点保留到最后。
有关更多详细信息,请参见failure detector。
集群单例
群集单例是每个数据中心一个单例。如果在所有节点上启动ClusterSingletonManager并定义了3个不同的数据中心,则集群中将有3个活动的单例实例,每个数据中心中有1个实例。这是自动处理的,但要注意这一点很重要。将系统设计为每个数据中心一个单例,这使得该系统在数据中心之间的网络分区期间也可以使用。
单例是按数据中心而不是全局的原因是,当在每个数据中心使用一个领导者时,不能保证成员信息在各个数据中心之间是一致的,这使得很难选择单个全局单例。
如果需要全局单例,则必须选择一个数据中心来承载该单例,并且只能在该数据中心的节点上启动ClusterSingletonManager。如果另一个数据中心无法访问该数据中心,则无法访问单例,这是在选择一致性而不是可用性时的合理权衡。
默认情况下,单例代理将消息路由到自己数据中心中的单例,但是可以通过ClusterSingletonProxySettings中的dataCenter参数启动它,以定义它将消息路由到位于另一个数据中心中的单例。例如,在一个数据中心中有一个全局单例,从其他数据中心访问它时,这很有用。
这是为特定数据中心创建单例代理的方法:
如果使用自己的数据中心作为withDataCenter参数,它将作为自己的数据中心中的单例的代理,如果未提供withDataCenter,这也是默认值。
集群分片
集群分片中的协调器是集群单例,因此,如上所述,集群分片也是针对每个数据中心的。每个数据中心将有自己的协调器和区域,与其他数据中心隔离。如果您在所有节点上以相同的名称启动一个实体类型,并且定义了3个不同的数据中心,然后将消息发送到相同的实体ID,到所有数据中心中的分片区域,那么您将获得该实体ID的3个活动实体实例,每个数据中心一个。这是因为区域/协调器仅知道其自己的数据中心,并将在那里激活实体。它不知道其他数据中心中是否存在相应的实体。
特别是与基于单写程序原理的Akka Persistence一起使用时,避免在共享数据存储区的多个位置同时运行同一实体。这将导致数据损坏,因为不同实例存储的事件可能会交错,并且在以后的重放中将以不同的方式解释。有关活动持久性实体,请参阅Lightbend的Multi-DC Persistence
如果需要全局实体,则必须选择一个数据中心来托管该实体类型,并且仅在该数据中心的节点上启动ClusterSharding。如果数据中心无法从另一个数据中心访问,则实体不可访问,这是在选择一致性而不是可用性时的合理权衡。
默认情况下,群集分片代理将消息路由到其自己的数据中心中的分片区域,但是可以使用数据中心参数启动它,定义它应将消息路由到位于另一个数据中心中的分片区域。例如,当在一个数据中心中拥有全局实体并从其他数据中心访问它们时,这很有用。
这是为特定数据中心创建分片代理的方法:
它也可以与EntityRef一起使用:
管理全局实体的另一种方法是通过将消息路由到正确的区域来确保某些实体ID仅位于一个数据中心中。 例如,路由功能可能是将奇数实体ID路由到数据中心A,甚至将偶数ID路由到数据中心B。在将消息发送到本地actor之前,您要确定应将其路由到哪个数据中心。 如上所述,可以使用分片代理发送另一个数据中心的消息,并将自己的数据中心的消息发送到本地。