作者
KonradMalawski来源
Swift官方博客,请参考原文一起阅读
很高兴为大家带来一个SwiftServer生态系统新的开源项目SwiftClusterMembership。这个库旨在促进Swift在服务端领域的发展:集群化多节点分布式系统。在这个库中,我们提供了可重用的,与运行时无关的成员协议实现,可以在各种集群中使用。
背景集群成员协议是分布式系统的关键构建块(例如计算密集型集群,调度程序,数据库,键值存储等)。发布这个软件包,我们的目标是使此类系统的构建更加简单,因为它们不再需要依赖外部服务来为其处理服务成员资格。我们还希望邀请社区来合作,并开发其他更多的协议。
成员协议的核心是需要回答“谁是我的同伴?”这个问题。在一个有延迟或丢失消息,网络分区以及有无响应但仍然“活跃”的节点的分布式系统中,这个看似简单的任务实际上并不简单。为该问题提供一个可预测的、可靠的答案是集群成员协议的主要任务。
实现成员协议可以采取多种策略,同时这也是一个可以不断研究和完善的有趣的领域。因此,集群成员这个package并不专注于单个实现,而是提供一个各种分布式算法协作的空间。
随着初始版本的发布,我们也开源了这类成员协议的一个实现:SWIM。
使用Swift实现的SWIMming我们开源的第一个成员协议实现是ScalableWeakly-consistentInfection-styleprocessgroupMembership[1]协议(简称SWIM),以及Lifeguard:LocalHealthAwarenessforMoreAccurateFailureDetection[2]这篇年的论文中提及的一些重要的协议扩展。
SWIM是一个gossip协议[3],其中对等节点定期交换自己对其它节点状态的观察结果相关的信息,最终将信息传播给集群中所有其它成员。这类分布式算法可以很好地防御信息的丢失、网络分区和其它类似问题。
在上层,SWIM的工作方式如下:
成员定期去ping一个随机选择的可预知的对等节点。它通过向对等节点发送.ping消息来完成该任务,并期望返回一个.ack。可以在下图中看到节点A如何去探测节点B。
交换的信息同时会携带一个gossip负载,该负载是信息发送方可探测的其它对等节点的部分信息,以及他们的成员状态(.alive,.suspect等等)
如果节点收到.ack,则目标对等节点被视为仍处于活动状态(.alive)。否则,目标对等节点可能已终止/崩溃或由于其它原因而没有反馈。
为了再次检查对等节点是否真的宕机,节点会通过向其它对等节点发送.pingRequest消息,以询问无响应对等节点的状态,然后向该对等节点直接发出.ping消息。
如果这些ping操作都失败了,则节点将收到一个.nack(negativeacknowledgement)消息,并且由于缺少.ack而导致探测的对等节点被标记为.suspect。
上述机制不仅用作故障检测机制,而且还用作gossip机制,该机制承载有关集群的已知成员的信息。这样,成员即使没有所有成员的信息,也可以最终了解其对等节点的状态。值得指出的是,这种成员资格视角是弱一致性的,这意味着不能保证所有成员是在给定的时间点对其它的成员的观察是否是相同的。不过,这是构建更高层工具和系统的坚实基础。
一旦故障检测机制检测到无响应的节点,该节点最终将被标记为.dead,导致其不可撤消地从集群中删除。我们的实现提供了一个可选的扩展,并添加了一个.unreachable状态,但是大多数用户会发现它不是必须的,并且默认情况下是禁用的。有关合法状态转换的详细信息和规则,可以参考SWIM.Status[4]或者下图:
SwiftClusterMembership实现协议的方式是通过提供协议的Instance。例如,SWIM实现被封装在与运行时无关的SWIM.Instance中,需要通过联网运行时与实例本身之间的某种粘合代码来“驱动”或“解释”。我们将这些实现的粘合代码称为“外壳”,并且该库有一个使用SwiftNIO的DatagramChannel实现的SWIMNIOShell,DatagramChannel通过UDP异步执行所有消息传递。替代实现可以使用完全不同的传输方式,也可以在其它现有的gossip系统上搭载SWIM消息。
SWIM实例还内置了对发出指标(使用swift-metrics)的支持,并且可以配置为通过传递swift-logLogger来记录内部详细信息。
示例:重用SWIM协议逻辑实现这个库的主要目的是在需要某种形式的进程内成员资格服务的各种实现之间共享SWIM.Instance实现。该项目的README[5]文件中详细记录了实现自定义运行时的信息,因此,如果您有兴趣通过某种不同的传输方式实现SWIM,请去那里看看。
实现新的传输可以归结为"fillintheblanks"练习:
首先,必须使用目标传输来实现对等协议:
publicprotocolSWIMPeer:SWIMAddressablePeer{funcping(payload:SWIM.GossipPayload,fromorigin:SWIMAddressablePeer,timeout:DispatchTimeInterval,sequenceNumber:SWIM.SequenceNumber,onComplete:
escaping(ResultSWIM.PingResponse,Error)-Void)//...}这通常意味着封装一些连接,通道或其他身份,并具有发送消息和在恰当时机调用适当的回调的能力。
然后,在对等方的接收端,节点必须实现以旧换新这些消息,并调用在SWIM.Instance中定义的所有相应的on回调。这些调用在内部执行所有SWIM协议指定的任务,并返回指令,这些指令很容易将