您的位置:首页 > 其它

AKKA集群规范

2017-10-18 19:55 176 查看
AKKA集群规范
注意:这个文档描述了集群的设计概念。
1 引言
    Akka集群提供了一个具有容错能力、去中心化、基于对等的集群成员资格服务,无单点故障,无单点瓶颈。它是使用gossip协议和自动故障检测做到这点的。
2 术语
节点(node):集群的一个逻辑上的成员。在一个物理机器上可能有多个节点。由hostname:port:uid元组定义。
集群(cluster):通过成员资格服务一起加入的一组节点。
领导(leader):集群中的单个节点,它扮演了领导的角色。用于管理集群的收敛和成员资格状态的转变。
3 成员资格
    一个集群由几个成员节点组成。每一个节点的标识是hostname:port:uid的一个元组。一个Akka应用分散在集群中,每一个节点代管应用的某些部分。运行在某个节点上的应用的集群成员和角色是解耦的。一个节点可以是不代管集群中任何角色的成员。要加入集群,就向集群中某个节点发送Join命令。
    节点标识符内部也包含一个UID,这个UID唯一标识在hostname:port上的角色系统实例。Akka使用UID能可靠地触发远程消亡监视。这意味着同样的角色系统只要它从集群中移除,就再也不能加入这个集群。要以相同的hostname:port的角色系统重新将加入集群,你必须停止角色系统,并用相同的hostname:port启动一个新的角色系统,这将接收一个不同的UID。
    集群成员资格状态是一个特殊化的CRDT,它意味着它由一个单调的合并函数。当并发修改发生在不同的节点上时,更新总是会被合并,会聚到相同的结果。
3.1 Gossip
    Akka中使用的集群成员资格是基于亚马逊的Dynamo系统,尤其是Basho的Riak分布式数据库中采取的方法。集群成员资格使用Gossip协议进行通信,集群的当前状态是在集群中随机传播的,倾向于传给那些没有看到过最新版本的成员。
3.1.1 矢量时钟
    矢量时钟Vector
clocks
是一种数据结构和算法,用于在分布式系统中生成部分排序的事件,违反因果关系检测。
    我们使用矢量时钟调和和合并在闲聊(gossiping)中集群状态的差异。一个矢量时钟是一组(node,
counter)对。每一次集群状态的更新都伴随着矢量时钟的更新。
3.1.2 Gossip会聚
    有关集群的信息在特定的时间点会聚到本地的某个节点。这个时间点就是当某个节点可以证明它所观测的集群状态已经被集群中其他的节点观测到。会聚这样实现的:通过传递一组已经在gossip期间看到当前状态版本的节点。这些信息被称为gossip概述中的可见集。当所有的节点都包含在可见集中时,会聚就发生了。

    当任何一个节点不可达时,Gossip会聚都不会发生。节点需要再次可达或者迁移到down状态或removed状态(参见Membership
Lifecycle
一节)。这只会阻塞leader执行其成员资格管理功能,不会影响集群中运行着的应用。例如,这意味着在网络分配期间,不可能向集群中增加更多的节点。这些节点可以加入,但是不能迁移到up状态,指导分配结束或者不可达节点被down掉。

3.1.3 故障检测
  故障检测器负责尝试检测一个节点对于集群中其他的节点是否是可达的。为此我们使用Hayashibara等实现的φ权责发生制故障检测器

  一个权责发生制故障检测器将监视和解释解耦。这使得它们适用于更广泛的场景,更加适合构建通用故障检测服务。它的思想是:通过计算从其它节点收到的心跳信息来维护一个故障历史记录,通过考虑多个因素、以及它们随时间的累积来做有依据的猜测,来对一个节点是up还是down做一个更好的猜测。它返回一个phi值代表一个节点down的可能性,而不是简单地对“这个节点是否down了?”这个问题做“是”或“否”的回答。
   这项计算所依据的阀值是由用户设置的。一个较低的阀值容易产生很多错误的猜测,但是当一个真正的故障(crash)发生时,它能保证快速发现。与此相对的是,高阀值会产生成少的错误,但是会花更多的时间才能检测到实际发生的故障。默认阈值为8,适合于大多数情况。然而,在云环境中,比如Amazon
EC2,该值可以提高到12,以应对在这样的平台上有时会发生的网络问题。
   在集群中,每个节点被一些(默认最大为5)其它节点监控,当其中任何一个监测点节检测到这个被监测节点不可达,这个不可达信息就会通过流言传播到节点的其它部分。换句话说,只要有一个节点标记某节点不可达,集群中的其它节点都会标记这个节点不可达。

   监控节点从一个哈希有序的节点环的相邻节点中选出。这是为了增加跨机架和数据中心监控的可能性,但是这个顺序(译注:即上一句中的“有序”)对于所有节点都是相同的,这样保证了完全收敛。
   心跳每一秒发送一次,每个心跳由一个请求/回复握手来实现,其中的回复被用于故障检测的输入。
   故障计测器也会检测是否节点再次可达。当所有的监视不可达节点的节点检测到它再次可达,集群会在gossip广播之后认为该节点可达。

  如果系统消息不能投递到某个节点,那么这个节点就会被隔离,然后它就不能从不可达状态恢复了。如果有太多不认识的系统消息(例如监视、终结、远端actor部署、由远程父Actor监督的actor故障),这是有可能发生的。那么这个节点需要迁移到down或removed状态(参见Membership
Lifecycle
一节),actor系统必须在该节点再次加入集群之前重启。

3.1.4 Leader
  在gossip会聚之后,集群的leader就可以确定了。没有leader的选举过程,leader总是可以广义地通过gossip会聚识别。leader只是一个角色,任何节点都可以是leader。而且它可能在两个会聚周期中改变。leader简单地就是能够担当领导角色的节点排序后的第一个节点,在这里leader更偏向于成员状态为up和leaving。(参见下面的成员资格的生命周期一节了解关于成员状态的更多信息)。

  leader角色就是将成员从集群中移入或者移出,将joining状态的成员修改为up状态,将exiting状态的成员修改为removed状态。当前,leader动作只有通过gossip会聚接收到一个新的集群状态时才被触发。

  如果进行了配置,leader也有权利根据故障检测器认为某个节点不可达时自动down掉这个节点。这意味着leader在配置的不可达时间之后,会将不可达的节点的状态自动设置为down。

3.1.5 种子节点
  种子节点被配置为新节点加入集群的联络点。当一个新节点启动的时候,它会向所有的种子节点发送一个消息,并向第一个应答的种子节点发送一个join命令。

  种子节点配置的值不会对运行中的集群本身有任何影响。它只与要加入集群的新节点有关,它帮助新节点找到发送join命令的联络点。新成员可以向集群中的任何成员发送这个命令,不只是种子节点。

3.1.6 Gossip协议
  一个push-pull gossip的变种被用来减少在集群中传播的gossip信息的大小。在push-pull gossip中,发送的是代表当前版本的摘要,而不是实际的值。gossip的接收者可以发送回它拥有的更新版本的值,也可以请求它持有的过期版本的新值。Akka使用单一的共享状态,以一个向量时钟来做版本控制。所以,Akka所使用的push-pull
gossip的变种使用此版本来只在需要时推送实际状态。

    每隔一段时间,默认为1秒,每个节点选择另一个随机的节点来发起一轮gossip.如果在seen set中的节点少于一半,那么集群会每秒gossip三次而不是一次。这样调整了gossip的间隔,使得在状态改变后,收敛过程的早期传播阶段加速。

    选择与哪个节点gossip是随机的,但是倾向于可能还没见到过当前状态版本的节点。在每轮gossip中,如果没有收敛,就使用0.8(可配置)的概率来与一个不在seen set中的节点gossip,也就是,它可能有一个更旧版本的状态。否则的话,就随机与任何活着的节点gossip.

    这种存在偏向的选择是一种在状态改变后,在接下来的传播阶段的后期加速收敛的方法。

    对于大于400(可配置, 建议根据实际表现配置)个节点的集群,0.8这个概率逐渐降低以避免太多并发的gossip请求压倒单个落后的节点。gossip的接收者通过丢弃进入mailbox时间太长的消息,来保护自己不受过多同时到达的gossip消息的损害。

    当集群处于收敛状态时,参与gossip的人只发送包含gossip版本的很小的gossip status信息给被选择的节点。只要集群有所改变(也就是不收敛),那么就会立即变回有偏向的gossip。

    gossip state或者gossip status(译注指某个节点在其集群成员关系生命周期中处于的状态)的接收者可以使用gossip版本(向量时钟)来决定:

    1. 它有一个gossip state的新版本,此时它会把这个新的gossip state发送给gossip的发送者。

    2. 它有一个过期的版本。此时,接受者会把它的gossip state给发送者来请求当前状态。

    3. 它有一个有冲突的版本,此时,这些不同的版本会被合并,然后发送回。

    如果发送者和接收者的版本相同,那么gossip state不会被发送或者请求。

    gossip的周期性性质对于状态变化有优良的批量效应,比如,往一个节点很快地连续加入多个节点将仅会带来一个要传播到集群其它节点的状态改变

    gossip消息使用protobuf序列化,并且使用gzip压缩以减少负载大小。

3.2 成员资格的生命周期
  节点一开始处于joining正在加入状态。一旦所有的节点都发现新节点正在加入集群(通过gossip会聚),leader将这个新成员的状态设置为up。

  如果某个节点以安全的、期望的方式离开集群,那么它将切换到leaving状态。一旦leader发现所有节点都已经知晓该节点处在leaving状态,leader就会把它迁移到exiting状态。一旦所有的节点都看到了它的exiting状态(会聚),leader将从集群中移除这个节点,并标记为removed状态。

  如果某个节点不可达,那么gossip会聚是不可能的,因此任何leader的动作也是不可能的(例如,允许某个节点成为集群的一部分)。要是想继续往前走,不可达节点的状态必须被改变。它必须再次变得可达或者被标记为down状态。如果该节点再次加入了集群,那么角色系统必须重启,再次走一遍加入程序。集群也可以在配置的不可达时间后,通过leader 自动down掉一个节点。

注意:当你启用了auto-down以及故障探测器,如果你不采用措施来关闭不可达的(shut down)节点,那么随着时间推移,你可能会得到很多单节点的集群。这遵循以下事实:不可达的节点很可能也会把集群中的其它节点视为不可达,因此成为自己的leader, 并且形成自已的集群。 

正如所述,如果一个节点不可达,那么gossip会聚是不可能的,因此任何leader动作也是不可能的。通过设置akka.cluster.allow-weakly-up-members,就有可能让新的joining节点在会聚不可能时被提升。这些Joining节点会被提升为WeaklyUp。一旦gossip会聚实现,leader就会将其从WeaklyUp状态改为Up。

注意,其他网络分区的成员不知道新成员的存在。你应该无法计算WeaklyUp成员的数量。

3.2.1 成员状态转换图(akka.cluster.allow-weakly-up-members=off)

3.2.2 成员状态的转换图(akka.cluster.allow-weakly-up-members=on)

3.2.3 成员状态
· joining(正在加入)

当加入集群时的过渡状态。

· weakly up(弱加入)

当网络分区时的过渡状态(只有akka.cluster.allow-weakly-up-members=on时才会出现)

· up(加入)

正常的运行状态

· leaving / exiting(离开/退出)

在优雅删除期间的状态

· down(挂了)

标记为未加入(不再是集群决策的一部分)

· removed(移除)

墓碑状态(不再是一个成员)

3.2.4 用户动作
· join(加入)

把单个节点加入集群:可以明确指定,或在启动时自动加入,只要在配置中指定了要加入的节点。

· leave(离开)

告诉一个节点要优雅地离开集群。

· down()

标记一个节点为down

3.2.5 Leader动作
领导节点leader有下面几个责任:

· 成员的移入或移出集群

o joining -> up

o exiting -> removed

3.2.6 故障检测和不可达
· 故障检测

监控节点的某个节点故障检测被触发将导致被监控的节点被标记为不可达。

· 不可达

不可达不是一个真正的成员状态,更多的是一个集群不能跟着这个节点通话的标志信号。在不可达之后,故障检测可能再次检测到它不可达,从而移除这个标志。

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: