ONOS 主要包括两类一致性机制:最终一致性和强一致性。
最终一致性采用乐观异步复制和基于 Gossip 的熵减方式来实现,乐观异步复制可以高效的实现最终一致,但是一旦集群中发生节点脱离集群或者重启的情况整体集群就会出现越来越失序的现象,基于 Gossip 的熵减方案就是为了解决此类问题,集群中的节点定期(通常间隔三到五秒)地随机选择一个节点进行数据同步,大多数情况下,熵减互动是平常的,因为每个控制器已经知道发生在网络中的每一个事件。
但是,当一个控制器状态稍微漂移时,这个机制很快就会检测到这个状态,并使控制器重新同步。 这种方法还具有快速将新加入的控制器和其他的控制器进行同步的好处。 新加入的控制器与现有控制器之间的第一次熵减互动将很快实现节点同步,而不需要单独的备份/发现机制。
在动态扩容的情况下,动态节点的加入会对最终一致性产生影响,表现为新的节点加入集群,在和其他节点的熵减交互以及乐观复制中最终和整体集群达到一致。这部分涉及的子系统包括 Device 和 Link 子系统。
而 Device,Link 子系统也会影响到 Topo 子系统,所以在进行节点动态扩容时,新加入节点在实现最终一致的过程中如果不承载业务的话影响较小。
强一致性的保障通过 Raft 算法来实现,ONOS 考虑到容错和性能的通盘考虑,选择了分区机制和备份冗余机制。
分区机制是指 ONOS 对任意一个支持强一致性的分布式原语(主要包括其分布式数据结构)支持分区机制,而在每一个分区中支持多个节点之间的备份冗余,实现了 CAP 理论的折衷性考虑。
在分布式系统中时钟是个重要的概念,ONOS 选取了以 MasterShip Term 和本地事件序列号的方式来进行统计。其理论依据如下:
网络控制器的控制离不开设备,所有的网络事件都是最终都能关联到设备上
MasterShipTerm 是全局强一致的,依赖这个数据做时钟的可靠性高
控制器依赖从设备收上来的信息来发出网络事件,而真正抛出事件的只有 Master,Master 维护着对应设备上报事件的序列号,在每一个 Term 周期内从 0 开始单调递增
当前 ONOS 大部分子系统都采用的是强一致性的方式,包括:FlowRule, Host, MasterShip 等,其中 MasterShip 是整体集群数的强一致,其他子系统是基于 Partition 内部节点的强一致。所以 ONOS 集群的宕机风险和 Partition Member 数量有关,如果 Partition Member 只有三个节点,那么两台设备宕机就会造成系统问题。
在节点动态加入集群的场景下,最大的问题是要防止出现脑裂,所谓脑裂就是一个集群中同时出现两个 Leader 的场景,在集群节点减少的情况下不会出现,但是在集群添加节点时会出现这种场景,如下图所示:
在上图所示的场景之下,假如新的 Server 先以新配置启动,而旧的 Server 逐步以新配置运行,此时会存在新配置的大多数和旧配置的大多数共存的情况,操作不慎会导致集群存在两个 Leader 进而脑裂的情况。
ONOS 的 Raft 算法采用 Copycat 实现,其支持动态节点的加入,但是这个方法不同于 Raft 论文中提到的两阶段添加的方案,而是采用了单节点添加方案来避免出现脑裂的情况,这样使得方案更简单但是相对操作会麻烦一些。另外在新加入节点开始进行数据同步时,业务要尽量避免写入。以免影响读写性能。
带状态重启也是生产环境中非常重要的一点。ONOS 大部分分布式数据结构都是支持持久化的,部分不支持的主要是最终一致性数据结构。 这其中 ECMap 必须配置持久性选项才能将条目写入磁盘,否则在集群关闭时会丢失。
但是大多数分布式原语(强一致性)使用了 Raft 集群,并且它们是持久化的。 ConsistentMap,ConsistentTreeMap,DocumentTree,DistributedSet,LeaderElector 以及这些基元的所有 Async * 版本都使用单个 Raft 分区或所有 Raft 分区。 这些原语有效地由持久的复制日志支持,该日志将从该 /data 目录中读取,并在重新启动群集时重播。
本文作者:江睿