极客油画

swim

SWIM: Scalable Weakly-consistent Infection-style process group Membership Protocol

swim解决的问题:“这个节点还活着吗?”,gossip解决的问题:“如何让所有节点都知道某条信息?”
swim解决了“探测”问题,而gossip可以辅助swim解决“传播”问题。它们是互补的,而非对立的。

在分布式系统中,所有参与进来的进程,都需要最终一致地感知进程组成员(membership)的信息。

有没有一种方案是peer-to-peer去中心化的,并且随着进程组成员的增多,某个成员从崩溃到被系统检测出故障的时间基本保持一致,每个成员处理的消息数量也基本保持不变。

广播是all-to-all,负载太高,pass;ip多播,虽然是some-to-some,但是需要配置硬件,运维太复杂,pass

SWIM具备以下特点:

  • 每个组成员的消息负载恒定
  • 在某个非故障进程中以(预期)恒定时间检测进程故障
  • 提供一个确定性的时间界限(作为组规模的函数),用于非故障进程检测另一个进程故障的本地时间
  • 以感染式(也称gossip式或流行病式)传播成员更新,包括关于故障的信息;组内的传播延迟随成员数量缓慢(对数级)增长
  • 提供一种机制,通过“怀疑”进程而不是直接“宣告”其失败来减少误报率

SWIN有两个组成部分:

  1. 故障检测组件:该组件用于检测成员的故障情况。
  2. 信息传播组件:该组件负责传播有关最近加入、离开或发生故障的成员的信息。

在 SWIM 协议中,每个节点在每个检测周期(Protocol Period)内,只会向 1 个随机节点发送 Ping 包

SWIM 协议通过“随机抽样”来降低网络开销,而不是像传统心跳那样广播给所有节点。具体流程如下:

  1. 单次探测(1 Ping): 每个节点在每个周期开始时,会从当前活着的成员列表中随机选择 1 个节点作为探测目标,向其发送 Ping 包。

  2. 间接确认(K 次 Ping-req): 如果 Ping 超时未收到 Ack,发起方会进入“怀疑阶段”。此时,它会随机选择 K 个节点(通常 K=3),请求它们帮忙探测目标节点。只有间接探测也失败时,才判定目标节点故障。

为什么只发 1 个?

  • 降低负载:如果每个周期向所有节点发 Ping,网络负载是 O(n²),无法扩展。只发 1 个,负载降为 O(n)。
  • 概率覆盖:虽然每次只查 1 个,但经过多个周期后,每个节点都会被其他节点探测到,保证了故障检测的覆盖性。

1 故障检测组件

故障检测组件的算法使用了两个参数:

  1. 协议周期 $T’$(以时间单位表示):这是指在分布式系统中执行一次探测操作的时间间隔。
  2. 整数$k$,这是故障检测子组的大小,即每次探测时选择的成员数量。

该协议不需要成员之间的时钟同步,并且只要 $T’$ 是各成员的平均协议周期,协议的特性就能保持不变。

SWIM-failure-detection

上图展示了在一个任意成员$M_i$上的工作流程。在每个长度为$T’$时间单位(基于$M_i$的本地时钟)的协议周期内,会从$M_i$的成员列表中随机选择一个成员(假设为$M_j$),并向它发送一个ping消息。然后,$M_i$等待来自$M_j$的回复ack消息。

如果在预设的超时时间内(由消息往返round-trip时间决定,并且小于协议周期)没有收到回复,$M_i$将通过间接indirectly探测$M_j$来进一步确认。此时, $M_i$随机选择$k$个成员,并向每个成员发送一个ping-req($M_j$)消息。这些非故障的成员在接收到该消息后,将向$M_j$发送ping消息,并将从$M_j$收到的ack消息(如果有的话)转发回$M_i$。

在当前协议周期结束时,$M_i$检查是否直接从$M_j$或者通过$k$个成员的其中一个间接收到了任何ack消息。如果没有收到任何ack消息,$M_i$将在其本地成员列表中声明$M_j$已失败,并将此更新传递给信息传播组件(Dissemination Component)。

用于启动间接探测的预设超时时间是基于对网络内往返时间分布的估计,例如可以使用平均值或第99百分位数。第99百分位数是指在一个数据集中,其值低于该数据集中的99%的数据点的数值。换句话说,它是这样一个数值,在该数值之下的数据占全部数据的90%,而在该数值之上只有10%的数据。请注意,协议周期$T’$至少是往返时间估计值的三倍。本篇论文采用的是平均值。注意,ping,ping-req,ack消息的大小被一个常数所限制,并且和group的大小无关。

使用间接探测ping-req的原因是为了避免$M_i$和$M_j$之间网络路径上的任何拥塞congestion影响;这种拥塞可能会导致原始ping消息或其ack被丢弃。分析过程如下:

  • 如果每个成员有一个大小为$n$的成员列表,其中无故障的比例是$q_f$,那么在一个协议周期中任意成员被选为 ping 目标的可能性是$1-(1-\frac{1}{n}\cdot q_f)^{n-1}$,这个概率会迅速下降到$1-e^{-q_f}$(随着$n \to \infty$)。
  • 因此,任意成员发生故障到其被组内某个进程检测出来的时间最多为 $T’\cdot \frac1{1-e^{-q_f}}$ 。这给出了根据应用指定的预期检测时间来估计协议周期长度的方法。
  • 如果 $q_{ml}$ 是网络中每个数据包及时送达的概率,并且各个数据包的送达是相互独立的,那么在一个协议周期内,任意一个非故障成员被错误地检测为失败的概率为 $q_f\cdot (1-q_{ml}^2)\cdot (1-q_f\cdot q_{ml}^4)^k\cdot \frac{e^{q_f}}{e^{q_f}-1}$ 。
  • 这个失败检测器满足强完整性:一个故障成员最终会被每个非故障成员选为ping目标,并从其成员列表中删除。
  • 由协议施加的每个成员的预期消息负载是一个不随组大小变化的常数,并且在所有成员之间是对称的。这个负载可以根据$k$的估计值计算出来,$k$配置可以被应用程序所设置。
  • 这些性质都不依赖于组大小n。

2 信息传播组件

当检测到另一个组成员失败时,进程只需将此信息failed($M_j$)多播给组内的其他成员。接收到此消息的成员会从其本地成员列表中删除该失败成员的信息。

关于新加入成员或自愿离开成员的信息也以类似的方式进行多播。然而,对于一个进程要加入组,它需要知道组中的至少一个联系成员。这可以通过多种方式实现:如果组与一个众所周知的服务器或IP多播地址关联,则所有加入join请求都可以被导向到相应的地址。在没有这种基础设施的情况下,加入join消息可以进行广播,并且听到广播的组成员可以概率性地决定(例如通过抛硬币)是否回复该消息。或者,为了避免多个成员回复造成的混乱,可以在组内维护一个静态协调者来处理组加入请求。实际上,存在多个协调者不会影响协议的正确性,只会导致对加入请求有多个回复。多个协调者的发现和解决resolution可以随着时间的推移通过传播组件来完成。。在当前版本的SWIM中,我们选择维护一个协调者,尽管没有理由排除其他任何策略。

4. 一个更稳健和高效的SWIM

第3节描述了基本的SWIM协议,该协议使用网络多播传播成员更新(由成员加入、离开或故障引起)。然而,网络多播原语如IP多播等仅是尽力而为的服务——网络中的消息丢失可能导致任意且相关的成员变更信息在任何组成员处未能收到。在第4.1节中,我们描述了一个传播组件的设计,该组件将成员更新附加在由失败检测器协议发送的ping和ack消息上。这完全消除了传播组件产生额外数据包的需求(例如,多播)。因此,SWIM生成的数据包就只有ping、ping-reqs和acks,从而为每个组成员提供了恒定的预期消息开销。这种方法导致了一种类似感染的传播方式,具有对数据包丢失的鲁棒性robustness和低延迟的相关优势。

尽管基本的SWIM失败检测器协议具有可计算的准确性,但它可能会受到一些缓慢进程的影响(例如,由于缓冲区溢出而丢失大量数据包的进程),这些进程可能会错误地将其他正常的进程声明为故障。此外,进程可能在短时间内受到干扰,例如位于过载主机上的进程。这可能导致该进程错过及时回复收到的ping的机会,并被错误地宣告为失败。

第4.2节介绍了怀疑机制,即对于不响应ping消息的进程(由第3节所述的SWIM失败检测器协议生成),不会立即被宣布为“故障”。相反,该进程会被标记为“可疑”,并将此信息通过传播组件在组内传播。经过预设的超时时间(我们将在第5节讨论这个参数的值),如果疑似故障的进程仍未回应,则会被正式宣告为“故障”,且这一信息会传播给组内的所有成员。然而,如果在超时之前,疑似故障的进程对ping请求作出了回应,这一信息将以“存活”消息的形式传播给组内成员。这样,该进程在其未离开或重新加入组的情况下,在不同成员的成员列表中得到更新。

因此,这种预设的超时有效地在增加故障检测时间和减少误报频率之间进行了权衡。

基本的SWIM失败检测协议保证最终能够在每个非故障组成员$M_j$处检测到任意进程$M_i$的失败。然而,它并没有对从一个任意成员$M_i$失败到另一个任意成员$M_j$检测到该失败的时间(以成员$M_j$处的本地协议轮流次数计算)提供确定性的保证。第4.3节描述了对原始SWIM失败检测器协议的修改,该修改保证了所谓的时间有界完整性属性;即从故障发生到在成员$M_j$处检测到该故障的时间间隔不超过组大小的两倍(以协议周期数计算)。

4.1 感染式传播组件

第3节的基本SWIM协议通过使用多播原语在组内传播成员的更新。硬件多播和IP多播在大多数网络和操作系统上都是可用的,但由于管理原因等,它们很少被启用。那么,基本的SWIM协议将不得不使用成本较高的广播,或者效率低下的点对点消息传递方案,以将成员更新传播给所有组成员。此外,由于这种多播是不可靠的,成员变更只能以尽力而为的方式传播给组内的成员。

相反,增强的SWIM协议完全消除了对外部多播原语的使用。它通过将需要传播的信息附加在由失败检测器生成的ping、ping-req和ack消息上来实现这一点。我们称这种传播机制为“感染式”传播机制,因为信息以类似于社会中八卦或普通人群中传染病的传播方式扩散。请注意,这种实现方式的传播组件不会生成任何额外的数据包(如多播)——所有传递给该组件的“消息”都是通过附着在失败检测组件的数据包上来进行传播的。

以下是在一个由$n$个成员组成的同质混合群体中,从一个初始感染者开始的传染病传播的确定性分析。在每个时间单位接触率为$\beta$的情况下,感染成员的(预期)数量$x$(最初为1)与时间$t$之间的关系可以表示为:

$$\frac{dx}{dt} = \beta\cdot x \cdot (n-x) \Rightarrow x = \frac{n}{1+(n-1)e^{-\beta nt}}$$

在我们的感染式传播组件中,通过ping和ack消息传播成员更新的过程可以以类似的方式进行分析。将协议周期视为一个时间单位,接触率 $\beta$ 是任意一对已感染和未感染成员之间发生接触的概率,并且等于 $[1-(1-\frac1{n})^2]=(\frac2{n}-\frac1{n^2})$。这给我们提供了 $x = \frac{n}{1+(n-1)^{e^{-(2-\frac1{n})^t}}}$ 。

这样的流行病过程在组内以指数级速度传播;经过 $t=\lambda\cdot \log n$ 轮协议后,其中$\lambda$是一个参数,则预期的感染成员数量是 $x=\frac{n}{1+(n-1)n^{-(2-\frac1{n})\lambda}} \geq n\cdot(1-\frac1{n^{(2-\frac1{n})\lambda-1}})$ 。通过附载方式以感染式传播的成员更新将在 $\lambda\cdot \log n$ 个协议周期后传播到 $(n-n^{-((2-\frac1{n})\lambda-2)})$ 个组成员。简而言之,随着 $n$(以及 $\rightarrow\infty$)的增加,$x$ 的估计值趋向于 $(n-n^{-(2\lambda-2)})$。将$\lambda$值设置为一个小的常数就足以可靠地传播这种“传染病”——即使在组规模较小的情况下也是如此,我们在第5节的实验中证实了这一点。

关于实现的一些说明是必要的。在每个组成员$M_i$的 SWIM 协议层中,维护了一个最近成员更新的缓冲区,同时为缓冲区中的每个元素维护一个本地计数。本地计数指定了到目前为止该元素被$M_i$捎带传输的次数,并用于决定接下来要捎带哪些元素。每个元素最多会被捎带 $\lambda\cdot\log n$ 次。如果缓冲区的大小超过了单个 ping 消息(或 ack)能够捎带的最大元素数量,则优先选择那些被传播次数较少的元素。这是必要的,因为协议周期是固定的,而成员变化的速度可能会暂时超过传播的速度。在这种情况下,优先选择“较新”的缓冲区元素可以确保所有成员的变化至少感染一部分成员——当成员变更引入率降低时,这些变更将继续传播到组中的其他成员。

我们对该协议的实现维护了两个组成员列表:一个是没有被宣告为失败的成员列表,另一个是近期失败的成员列表。目前,从这两个列表中选择相同数量的元素用于捎带传输,但该方案可以进一步扩展,以适应进程加入、离开和失败速率的相对变化。

4.2 怀疑机制:降低误报的频率

在迄今为止描述的 SWIM 故障检测协议中,如果一个非故障的组成员 $M_j$ 被另一个组成员 $M_i$(错误地)检测为失败,可能是由于网络数据包丢失、$M_j$ 一段时间内处于休眠状态、或者 $M_i$ 是一个缓慢的进程,那么 $M_j$ 将被宣布为组内的失败成员。换句话说,一个完全健康的进程 $M_j$ 会因为被错误地检测为失败而遭受非常严重的惩罚,即被迫在第一次被误判为失败时就退出组。这导致在故障检测中出现高比例的误报。

我们通过修改 SWIM 协议,在基本的 SWIM 故障检测协议检测到故障时运行一个称为“怀疑子协议”(Suspicion subprotocol)的子协议,来减轻这一问题的影响。

“怀疑子协议”的工作原理如下:考虑一个成员 $M_i$,在当前协议周期内选择成员 $M_j$ 作为 ping 目标,并运行基本的 SWIM 故障检测协议周期。如果 $M_i$ 没有收到任何确认(无论是直接还是通过间接探测子组),它不会立即将 $M_j$ 声明为失败。相反,$M_i$ 会在 $M_i$ 的本地成员列表中将 $M_j$ 标记为“疑似”成员。此外,一条 {Suspect $M_j$: $M_i$ suspects $M_j$} 的消息会通过传播组件(在我们的系统中是以传染式的方式)在组内进行传播。任何接收到此类消息的组成员 $M_l$ 也会将 $M_j$ 标记为“疑似”。被怀疑的成员仍然保留在成员列表中,并且在 SWIM 故障检测协议的 ping 目标选择操作中,它们会被视为与非故障成员类似。

如果成员 $M_l$ 在基本 SWIM 协议的正常运行过程中成功 ping 通了一个被怀疑的成员 $M_j$,它会在其成员列表中取消对 $M_j$ 的怀疑标记,并通过传播组件(在我们的系统中以传染式的方式)在组内传播一条 {Alive $M_j$: $M_l$ knows $M_j$ is alive} 的消息。这样的 Alive 消息会在接收成员的成员列表中取消对疑似成员 M_j 的怀疑标记。请注意,如果成员 $M_j$ 收到一条怀疑它的消息,它可以开始传播一条 Alive 消息,以澄清自己并未发生故障。

成员列表中的“Suspected”条目会在预设的超时时间后过期。如果在某个成员 $M_h$ 处怀疑 $M_j$,并且在收到 Alive 消息之前该条目超时,$M_h$ 会将 $M_j$ 声明为故障,将其从本地成员列表中移除,并开始通过传播组件传播 {Confirm $M_j$: $M_h$ declares $M_j$ as faulty}消息。该消息会覆盖任何之前的“Suspect”或“Alive”消息,并触发从所有接收者recipients的成员列表中删除 M_j 的级联操作。

这种机制降低了(但并未完全消除)故障检测中的误报率。还要注意,原始协议的强完备性属性仍然成立。怀疑某个已故障进程 $M_j$ 的进程发生故障可能会延长检测时间,但最终检测是能够得到保证的。

根据上述讨论,在本地成员列表中与被怀疑成员$M_j$对应的条目上:

  1. Alive消息会覆盖Suspect消息的影响
  2. Confirm消息则会同时覆盖Suspect消息和Alive消息的影响

然而,在一个成员的生命周期内,它可能会被多次怀疑和解除怀疑。这些多个版本的怀疑消息(Suspect messages)和存活消息(Alive messages)(都与同一个成员 $M_j$ 相关)需要通过唯一标识符加以区分。这些标识符通过在成员列表的每个元素中使用一个虚拟的 化身编号(incarnation number) 字段来提供。化身编号是全局的。成员 $M_i$ 的化身编号在加入组时初始化为 0,并且只有在 $M_i$ 收到(通过传播组件)关于自己在当前化身中被怀疑的信息时,才可以递增该编号。此时,$M_i$ 会生成一条包含其标识符和递增后的化身编号的存活消息(Alive message),并通过传播组件将此消息传播到整个组。

因此,怀疑消息(Suspect)存活消息(Alive)确认消息(Confirm) 除了包含成员的标识符外,还包含该成员的化身编号。这些消息之间的优先级顺序及其对成员列表的影响规定如下:

  • ${Alive\ M_l,inc = i}$ 覆盖:
    • ${Suspect\ M_l,inc = j}, i>j$
    • ${Alive\ M_l,inc = j}, i>j$
  • ${Suspect\ M_l,inc = i}$ 覆盖:
    • ${Suspect\ M_l,inc = j}, i>j$
    • ${Alive\ M_l,inc = j}, i\geq j$
  • ${Confirm\ M_l,inc = i}$ 覆盖:
    • ${Alive\ M_l,inc = j}, any\ j$
    • ${Suspect\ M_l,inc = j}, any\ j$

很容易看出,这些优先级顺序和覆盖规则维持了故障检测组件所需的正确性属性。熟悉诸如 AODV 等自组网路由协议的读者会注意到,它们对目标序列号的使用与我们的化身编号机制之间存在相似之处。

优先级规则和传染式传播组件还能够适应多个其他进程对某个进程的怀疑。优先级规则不依赖于怀疑的来源,而传染式传播方式在存在多个消息源时,能够更快地传播一条消息(怀疑、存活或确认),并且每个进程的通信开销保持稳定,与感染源数量无关(单源传播和多源传播场景下进程级开销相同)。

4.3 轮询探测目标选择:提供时间有界的强完备性

第 3 节中描述的基本 SWIM 故障检测协议能够在平均恒定数量的协议周期内检测到故障。尽管每个进程的故障最终都会被其他所有非故障进程检测到(最终强完备性),但如果组内选择 ping 目标的方式存在某种极端情况,可能会导致在组内任何地方首次检测到该进程故障的时间出现较大延迟。在最坏的情况下,这种延迟可能是无界的,因为故障进程可能永远不会被任何其他非故障进程选为 ping 的目标。

这个问题可以通过以下对协议的修改来解决。成员$M_i$的故障检测协议通过维护当前成员列表中已知元素的一个列表(直观上可以理解为一个数组)来工作,但选择 ping 目标时不是从该列表中随机选择,而是以轮询的方式进行。此外,新加入的成员会被插入到成员列表中的一个随机均匀选择的位置。在完成对整个列表的一次遍历后,$M_i$会将成员列表重新排列为一个随机顺序。

考虑成员$M_i$执行上述修改后的 SWIM 协议的过程。一旦另一个成员$M_j$被加入到$M_i$的成员列表中,它将在 $M_i$ 的成员列表的每次遍历中恰好被选为 ping 目标一次。如果成员列表的大小不超过$n_i$,那么同一目标的连续两次选择之间最多相隔 $(2\cdot n_i - 1)$ 个协议周期。这限制了$M_i$检测任何成员进程故障的最坏情况检测时间,从而满足了时间有界完备性(Time Bounded Completeness)属性。

通过这种优化,原始协议的平均故障检测时间得以保留,因为组内不同成员的成员列表随机化会导致每个成员选择 ping 目标的分布保持相似。

5. 性能评估原型

SWIM 协议的原型是基于 Winsock 2 API 实现的,并在由运行 Windows 2000 的大批商用 PC 组成的大型集群中进行了测试。该 PC 集群由 16 台 450-MHz 的戴尔 PII、16 台 1-GHz 的 IBM x220,以及一些双节点和四节点(200-MHz 至 500-MHz 的 PII 和 PIII 处理器)组成,这些节点通过无外部负载的 100 Mbps 以太网进行通信。每个节点最多包含一个进程组成员。

实验参数设置如下:被选中用于发送 ping-reqs 请求的成员数量为 $K=1$,协议周期设置为 2 秒。每个感染式传播(成员更新)最多会被附带在每个成员发送的 $3\lceil\log(N+1)\rceil$ 条消息中。通过这样的参数设置,我们的实验运行中没有观察到成员出现永久性部分成员列表或组的非自愿分区现象。被怀疑成员被宣告为失败的怀疑超时时间也设置为2秒。

我们比较了协议的三个不同版本:(1) (SWIM:Basic)第 3 节中描述的基本 SWIM 协议,经过第 4.3 节中描述的轮询机制修改;(2) (SWIM+Inf.)带有感染式传播组件的 SWIM 协议(如第 4.1 节所述);以及 (3) (SWIM+Inf.+Susp.)在 SWIM+Inf. 基础上添加了第 4.2 节中描述的怀疑子协议扩展。

SWIM 协议发送的所有点对点消息均为 UDP 数据包。在 SWIM:Basic 中,最大消息有效载荷大小为 15 字节,而在 SWIM+Inf. 和 SWIM+Inf.+Susp. 协议中,最大消息有效载荷大小为 135 字节(因为每条消息最多附带 6 条成员更新信息)。

5.1 消息负载

SendAndReceiveOverheads

上图展示了在任意组成员处由SWIM故障检测器测量到的消息发送和接收的负载(为清楚起见,点已水平扰动)。这些数据是在超过40个协议周期的时间段内采集的。在组规模达到55个成员之前,平均开销保持在2.0左右。这与分析估计相符,因为在每个协议周期中,一个组成员会发送一个ping消息并接收相应的ack消息,同时预期接收一个ping消息并发送相应的ack消息。标准差条形图表明典型的消息开销保持较低,例如,在N=28个成员时,以0.99的概率,每个协议周期内发送消息的开销小于5条消息。

回顾我们在第4节中的描述,该图展示了在SWIM+Inf.和SWIM+Inf.+Susp中每个成员的总消息开销,而在SWIM:Basic中仅显示基础负载(其中额外的多播用于传播成员更新)。

5.2 成员更新的检测与传播延迟

下图展示了,三种协议在不同组大小下,进程故障首次检测时间(点)以及在所示区间内获得的平均值(深色水平线)和区间(浅色垂直线)。同时展示了第3.1节中的分析估计值。

timeToFirstDetectionOfProcessFailure

上图显示了从进程故障到其首次被某个非故障组成员检测到之间的平均时间。平均检测时间似乎与组大小无关,因此与分析估计相符。在 SWIM:Basic 协议中,纵轴(垂直轴)的数值对应于 进程发生故障的时刻 到 其故障通知被组播(multicast)的时刻 之间的时间间隔。在SWIM+Inf.和SWIM+Inf.+Susp.协议中,这些值分别对应于故障与故障通知传播开始、怀疑通知传播开始之间的时间间隔。

下图展示,在SWIM+Inf.和SWIM+Inf.+Susp.协议中,感染在组内传播的延迟。所示的点对应于不同组成员首次接收到感染的时间。

LatencyOfSpreadOfInfection

上图展示了在感染式传播成员更新的过程中,延迟随组大小变化的情况。传播的中位延迟始终仅为几个协议周期,并且似乎随着组大小的增加而缓慢上升(回想一下,第 4.1 节的分析预测了平均感染时间随组大小呈对数变化)。

下图展示在 SWIM+Inf.+Susp 协议中使用的怀疑超时时间。

suspicionTimeout

上图展示了怀疑超时时间 $(3\lceil\log(N+1)\rceil)$,且仅适用于 SWIM+Inf.+Susp 协议。

从上面三幅图中可以得到一个进程失败到其被移除出其他成员的成员列表之间的平均时间。对于 SWIM:Basic 协议,这是第一个图中绘制的值加上一次网络多播所需的时间。对于 SWIM+Inf. 协议,这是第一幅图和第二幅图纵轴上分布的总和,而对于 SWIM+Inf.+Susp. 协议,该延迟是三幅图分布的总和。

5.3 故障检测误报

EffectWith10percentPacketLoss

SWIM+Inf.+Susp. 协议产生的故障误报数量微乎其微。为了评估感染机制和怀疑机制的相对优势,我们模拟了一个高达 10% 的人为丢包率。每个数据包(包括 SWIM:Basic 中的每次网络多播)在接收端以该丢包概率被丢弃。在我们的实验中,17 个进程尝试依次加入一个 SWIM 组,如上图所示。该图展示了在持续丢包率的情况下,加入阶段期间及之后组大小的变化。图中的数据在 175 秒时被截断,这是在最后一次观测到成员变更之后。该图展示了怀疑机制的优势:尽管存在持续的丢包,SWIM+Inf.+Susp. 最终达到了 12 个成员的稳定组大小,而 SWIM:Basic 和 SWIM+Inf. 分别仅稳定在 2 个和 4 个成员。

上图的数据仅适用于所描述的实验设置。更激进的参数调整以及怀疑超时时间的设置将进一步提高这三种协议的鲁棒性。调整怀疑超时时间还提供了一个调节旋钮,可以在故障检测时间与误报频率之间进行权衡。

6. 救生员Lifeguard扩展

Lifeguard是SWIM的扩展,通过引入本地健康(local health)的概念,考虑到了本地故障检测模块可能存在问题。

任何分布式系统都必须解决的三个关键问题是:服务发现、故障检测和组件间的负载均衡。

SWIM 论文指出,对消息处理速度缓慢的敏感性是基本 SWIM 协议的一个问题。消息处理缓慢可能由多种因素引起,包括 CPU 争用、网络延迟或丢包等,这可能导致 SWIM 将健康的成员误判为故障——即所谓的误报故障检测。为了解决这个问题,SWIM 论文提出了一种名为“怀疑(Suspicion)”的子协议,该协议通过增加故障检测的延迟来减少误报的发生。

然而,即使使用了 Suspicion 子协议,在某些情况下,消息处理缓慢仍可能导致健康成员被标记为故障。当这种缓慢处理间歇性发生时,一个健康的成员可能会在被标记为故障和健康之间反复波动。在调试这些场景的过程中,我们对 SWIM 在处理消息处理缓慢方面的不足有了深入的认识,并找到了一种解决该问题的方法。我们采用的方法是让 SWIM 的每个故障检测器实例都考虑自身的健康状况,我们称之为“本地健康”(local health)。我们通过一组对 SWIM 的扩展来实现这一点,这些扩展被我们称为 Lifeguard。

6.1. 动机

SWIM 使用基于随机探测的故障检测和基于 gossip 的更新传播,以实现许多吸引人的特性:

  • 可扩展性。在 SWIM 中,首次检测到故障的预期时间、假阳性率以及每个组成员的消息负载均与组的规模无关。完全传播故障信息的时间随着组规模的增长呈对数增长。
  • 鲁棒性。由于该协议是完全去中心化的,因此任何一部分组成员同时发生故障或网络分区都可以被容忍。即使完全分区的子组也可以继续运行,并在重新建立连接后自动合并。
  • 易于部署和维护。该协议的完全去中心化特性意味着,一个潜在的新成员只需联系任何现有成员即可加入组,且当某个成员离开时,无需采取特别措施来保持系统的健康运行。
  • 实现的简洁性。SWIM 协议的状态和消息种类较少。由于它是点对点的,因此无需在初始配置或成员变更时设置或维护任何特殊结构(如领导者或层级结构)。

基于 gossip 的更新传播机制使 SWIM 具有弱一致性。也就是说,在某一时刻,不同的成员可能对组成员关系有不同的视图。在实际应用中,许多场景下弱一致性是可以接受的。如果无法接受,正如 SWIM 论文所指出的,可以通过在 SWIM 之上叠加一个一致视图来实现强一致性,该视图会对成员列表进行检查点记录。

SWIM 的故障检测子协议被认为容易受到消息处理缓慢的影响,这可能导致假阳性故障检测,即健康的成员被错误地标记为故障。这是一个严重的问题,因为将流量从某个成员转移出去,以及在其恢复健康后重新将其集成到系统中,通常都会带来一定的成本。

SWIM 论文通过引入一种“怀疑”(Suspicion)机制来解决这一问题,该机制在第三部分中有详细说明。然而,我们在开发和支持多种使用 SWIM 的系统时发现,即使有了 Suspicion 机制,在数据中心有时会遇到的条件下,假故障检测仍可能以较高的频率发生。当多个成员同时变慢时,问题会更加严重。即使是健康的成员,如果受到与缓慢成员交互的影响,也可能将其他健康的成员标记为故障。

我们调试此问题的场景包括:

  • 为稳定状态配置的 Web 服务器,但会经历突发的高流量负载。
  • 运行防火墙和其他边缘服务的入口节点遭受持续的分布式拒绝服务(DDoS)攻击,导致网络和 CPU 负载均较高。
  • 视频转码服务器被分配了过度占用可用 CPU 的工作负载。
  • 可突发性能实例(如 AWS T2 类和 Azure B 系列虚拟机)被分配了耗尽其 CPU 积分的工作负载,导致它们受到所运行的虚拟机管理程序的限制。

在这些以及其他场景中,受影响机器上 SWIM 消息的缓慢处理导致同一成员组中的健康机器被错误地判定为故障。我们在裸金属和虚拟化系统中、私有数据中心以及公有云环境中都遇到过这种情况。

图1展示了一项实验的结果,在该实验中我们复现了视频转码场景的特性。我们在微软Azure公有云的一个区域部署了由100台单核虚拟机(Standard_A1_v2规格)组成的集群,所有节点均部署了Consul代理(这是每个要加入SWIM组成员关系的节点必须运行的守护进程)。随后我们在部分虚拟机上运行了极端CPU密集型工作负载——具体使用Linux stress工具配置为启动128个进程,每个进程执行密集的数学运算循环。该负载持续运行5分钟。实验中我们记录Consul触发的所有成员故障事件,并在实验结束后通过日志分析统计误报故障检测的发生次数。

图1的x轴表示运行stress工作负载的机器数量,其范围从1台到32台(每台机器代表集群的1%)。对于每个压力负载机器数量,y轴展示了两项关联指标:

  • 总误报数:针对健康成员(未运行stress工作负载的节点)产生的故障事件,这些误报可能由集群中任意成员(包括正在运行stress负载的成员)触发
  • 健康成员误报:特指由健康节点(未运行stress负载)主动报告的关于其他健康节点的故障事件。这类误报尤为关键,因为事件涉及的双方——发起报告的代理程序与被报告的目标代理程序——实际上均处于健康状态。

每个指标均展示两组数据:一组是 Consul 运行未修改的原始 SWIM 协议时的结果,另一组是 Consul 运行集成 Lifeguard 机制的 SWIM 协议时的结果。

图1显示,在使用基础SWIM协议时:

  • 仅需1个过载节点就足以引发误报故障检测事件
  • 当4个节点(占集群4%)过载时,健康节点即可产生数百个误报
  • 该问题随过载节点数量增加而急剧恶化 相比之下,Lifeguard机制表现显著改善:
  • 需同时过载16个节点才会出现误报
  • 健康节点误报需32个节点同时过载才会发生
  • 两种误报发生率均远低于SWIM协议

虽然此类问题并非频繁发生,但一旦出现便可能造成严重破坏。我们通过分析多起被升级为最高优先级支持请求的此类故障案例,最终研发出了Lifeguard机制。

6.2. SWIM 和 memberlist

在本节中,我们首先回顾SWIM协议,随后介绍memberlist——这是我们用于评估Lifeguard机制的SWIM协议实现方案。

  1. SWIM

SWIM有两个组件:

  • 故障检测器:用于检测集群成员节点故障的组件
  • 传播组件:负责在集群中广播成员节点的加入、退出及故障状态更新。

该故障检测器沿用了既有研究成果,采用完全去中心化设计。每个集群成员以可配置周期(称为协议周期)为单位异步运行检测流程,具体机制如下:

  • 直接探测阶段: 每个协议周期内,各成员随机选取另一个成员进行健康检查
    • 向目标成员发送ping消息
    • 若在配置时间内未收到ack响应,则进入间接探测
  • 间接探测阶段: 发起检测的成员随机选择k个其他成员
    • 向这些成员发送ping-req请求
    • 接收方收到ping-req后立即向被检测成员发送ping
    • 任一间接探测成员若收到ack,需转发至原始检测成员
  • 故障判定: 若直到协议周期结束,既未收到直接探测的ack,也未收到任何间接探测转发的ack,则判定被检测成员故障

传播组件采用基于Gossip的机制,其运作原理如下:

  • 更新传播规则
    • 每个成员加入/离开/故障的更新事件
    • 会被传播 λlog(n) 次(n为集群已知规模,λ为可调参数)
  • 消息捎带机制
    • 通过故障检测协议的三种消息承载更新:ping消息,ping-req消息,ack消息
    • 零额外消息开销(完全复用现有通信)
  • 智能负载控制
    • 单条消息携带更新数受限于MTU等网络约束
    • 优先级策略:优先发送传播次数较少的更新,确保高负载时所有更新都能逐步扩散

在SWIM协议的基础实现中,当某个成员未通过故障检测时,会立即被标记为故障状态,并通过传播组件将故障信息广播至整个集群。然而,SWIM的原作者们发现该机制存在误报问题,并指出消息处理延迟是主要原因。为此,SWIM论文提出了Suspicion(存疑)机制作为解决方案。虽然该机制在论文中被描述为扩展功能,但实际上已成为SWIM协议的必要组成部分。如第I节所述,目前所有成熟的SWIM实现(包括我们讨论的三种主流实现)均内置了这一机制。

当启用Suspicion机制时,故障检测流程将按以下方式演进:

  • 可疑状态转换
    • 未通过健康检查的成员首先进入Suspected(可疑)中间状态
    • 通过传播组件广播suspect消息(Gossip协议)
  • 集群协同验证
    • 收到suspect消息的节点: ✓ 同步将目标成员标记为可疑状态 ✓ 继续传播该可疑状态
    • 在suspicion超时窗口内: ✓ 允许其他节点提供反证据推翻怀疑
  • 最终状态确认
    • 若超时后未被推翻: ✓ 广播confirm消息正式确认故障 ✓ 集群统一将成员状态更新为Faulty(故障)

存疑状态通过以下方式被推翻:当关于被怀疑成员的alive消息通过Gossip协议传播,并在任一存疑成员达到其怀疑超时之前抵达所有持有怀疑的成员时。SWIM论文描述了alive消息可能产生的两种机制:要么由持有怀疑的成员在一轮故障检测中成功探测被怀疑成员后产生,要么由被怀疑成员本身在收到关于自身的suspect或confirm消息后产生。然而在实际运行中,被怀疑成员必须自行传播关于自身的alive消息才能使推翻机制生效。

SWIM论文对基础协议的另一项改进是让每个成员从其已知成员列表中按轮询方式选择故障检测目标,而非完全随机选择。若不这样做,在最坏情况下首次检测延迟将无上限,这是由于(极其罕见的)情况:整个组成员中选择故障检测目标时反复未能选中故障成员。通过轮询探测,最坏情况得以限制。然而,每个成员的列表仍保持随机顺序,新成员被随机插入列表位置。因此,预期的首次检测延迟保持不变。

  1. memberlist

memberlist是SWIM协议的一个开源实现,被包括Consul、Nomad和Serf在内的工具所使用。memberlist实现了上述SWIM描述的所有功能特性。它还具有以下额外功能:

  • memberlist的故障检测器默认使用UDP协议进行直接探测和间接探测。但在通过UDP发起间接探测的同时,它还会尝试通过TCP进行直接探测。这有助于解决TCP流量能正确路由而UDP不能的情况,这是在网络配置中有时会遇到的一种异常状况。
  • memberlist增加了一个反熵机制,通过该机制每个成员会定期与另一个随机选择的成员通过TCP进行完整状态同步。采用了push-pull方法,使用版本号(incarnation numbers)来协调关于特定成员的状态冲突。这种完整状态同步以更多带宽使用为代价,提高了节点更快完全收敛的可能性。它对于加速从网络分区中恢复特别有帮助。
  • memberlist拥有一个独立于故障检测协议的专用gossip层。与SWIM类似,memberlist会将gossip消息(suspect、alive和confirm)捎带在故障检测消息(ping、ping-req和ack)上传输,但它也会定期单独发送gossip消息。这使得gossip速率可以独立于故障检测速率进行调节,并且如果需要的话可以比故障检测更快,从而加速收敛速度。
  • memberlist会保留故障节点的状态一段时间,以便在完整状态同步时传递这些故障节点的信息。这有助于集群状态更快达成一致收敛。

由于这些功能通常在memberlist的部署中默认启用,本文评估时也全部启用了这些功能。Butterfly和Ringpop同样实现了许多类似的附加功能。

6.3. LIFEGUARD救生员

在研究解决第二节所述问题的可能方案时,我们观察到Suspicion机制仍然依赖于消息的及时处理。具体而言,只有当所有怀疑该成员的节点都能及时处理推翻怀疑的alive消息时,对怀疑状态的推翻才能成功。因此,故障检测模块本身的消息处理延迟,正是导致SWIM的Suspicion机制未能抑制误报的主要原因。

我们还观察到,预期响应的缺失可能表明某个成员节点正在经历消息处理延迟,且特定成员节点的消息处理延迟事件很可能会在短时间内影响其与其他成员的多次交互。我们将这些现象视为该成员本地故障检测器实例健康状态的衡量指标,简称为本地健康度(local health)。

基于这些观察,我们设计了Lifeguard:这是一组对SWIM协议的扩展,使其成为自适应协议。Lifeguard通过本地健康度的启发式测量,让成员节点能够判断其故障检测器何时可能出现消息处理延迟,并据此动态调整超时参数以缓解时效性问题。

Lifeguard在设计上尽可能保持与SWIM相同的架构,仅在以下三个提供新特性的组件上与SWIM存在差异:

  • 本地健康感知探测(LHA-Probe)取代了SWIM故障检测器中具有固定探测周期和超时的探测阶段,取而代之的是基于该成员近期与其他成员的故障检测相关通信动态调整这些参数的探测机制。
  • 本地健康感知怀疑机制(LHA-Suspicion)取代了SWIM故障检测器中具有固定怀疑超时的怀疑阶段,取而代之的是采用动态超时的怀疑机制。每个新怀疑的超时初始值显著高于固定超时情况下的设定值,但随着对同一被怀疑成员独立怀疑的处理而逐步降低。
  • 伙伴系统(Buddy System)取代了SWIM的捎带消息选择器,采用优先通知被怀疑成员有关怀疑状态的机制,以缩短平均推翻时间,这有助于使本地健康感知探测和本地健康感知怀疑组件发挥更大效用。

这些组件的详细说明将在后续章节中展开。

6.3.1 本地健康感知探测(Local Health Aware Probe,简称LHA-Probe)

本地健康感知探针(LHA-Probe)取代了SWIM故障检测器中原有的固定探测周期和超时机制的探测阶段,转而采用一种动态调整方案——根据该成员近期与其他成员在故障检测相关通信中的表现来灵活调节探测参数。其反馈机制整合了多源数据:

  • 将已接收到的确认应答(ack)消息数量与已发出的探测请求(ping)及间接探测请求(ping-req)数量进行对比。若出现确认应答缺失,可能是由于本地节点响应迟缓所致,尤其在多次出现该情况时更为典型。
  • 需要反驳对其自身的怀疑,这表明该成员可能没有及时处理最近的ping消息。
  • 局部健康感知探测(Local Health Aware Probe)在故障检测协议中添加了一条“nack”消息,该消息会在间接探测失败时发送。这为发起间接探测的成员提供了一种检查机制——即使它们间接探测的目标节点无响应,该机制也能让发起者确认其调用的k个成员是否在持续返回及时响应。

这些不同的反馈来源被整合到一个本地健康乘数(Local Health Multiplier,简称 LHM)中。LHM 是一个饱和计数器,其最大值为 S,最小值为零,这意味着它不会超过 S 或低于零。以下事件会导致 LHM 计数器发生指定的变化:

  • Successful probe (ping or ping-req with ack): -1
  • Failed probe +1
  • Refuting反驳 a suspect message about self: +1
  • Probe with missed nack: +1

LHM 的当前值用于按如下方式设置探测间隔和超时时间: $$ProbeInterval = BaseProbeInterval.(LHM(S)+1)$$ $$ProbeTimeout = BaseProbeTimeout.(LHM(S)+1)$$

ProbeInterval 是对连续随机选择的节点进行存活探测的时间间隔,而 ProbeTimeout 是接收某个探测的确认(ack)的超时时间。在 memberlist 的实现中,我们将 BaseProbeInterval 设置为 1 秒,将 BaseProbeTimeout 设置为 500 毫秒。S 的默认值为 8,这意味着探测间隔和超时时间最多会分别退避到 9 秒和 4.5 秒。

6.3.2 本地健康感知怀疑机制(Local Health Aware Suspicion)

本地健康感知怀疑机制(LHA-Suspicion) 替代了 SWIM 故障检测器中具有固定怀疑超时时间的怀疑阶段,采用了动态超时时间的机制。每个新怀疑的超时时间初始值显著高于固定超时的情况,但会随着来自其他成员(针对同一被怀疑成员)的独立怀疑被处理而逐渐减少。通过这种方式,只要本地成员能够及时接收和处理 gossip 消息,超时时间将降至其最小值。相反,对于未能及时接收和处理 gossip 消息的成员,怀疑超时时间将保持较高的水平。

给定怀疑的超时时间计算如下: $$SuspicionTimeout = max(Min,Max-(Max-Min)\frac{\log(C+1)}{log(K+1)})$$

其中:

  • Min 和 Max 分别是怀疑超时时间的最小值和最大值。有关它们的配置讨论,请参见第 V-C 节
  • K 是在将怀疑超时时间设置为 Min 之前需要接收到的独立怀疑数量。我们默认 K 为 3。
  • C 是自本地怀疑被触发以来,接收到的关于该成员的独立怀疑数量。

每当收到一条怀疑消息,且该消息代表了针对同一成员的一个之前未见过的独立怀疑时,超时时间将会重新计算。此时,当前的怀疑计时器会被取消,并替换为基于新缩短超时时间的剩余时长重新设定的计时器。如果该剩余时长已过,则立即触发超时。

使用对数衰减的方式,使得每次超时时间的缩减幅度比上一次更小,因为收到了更多的独立怀疑消息。其背后的直观理解是,第一条独立消息能够最大程度地提升对消息是否及时接收的信心,而后续的每条消息对信心的提升则逐渐减少。

为了使独立怀疑更为普遍,当启用 LHA-Suspicion 时,关于同一成员的前 K 条独立怀疑会被重新传播(re-gossiped)。每条怀疑消息会被发送 λlog(n) 次,因此如果收到了 K 条更多的怀疑消息,最多会发送 (K + 1)λlog(n) 条消息。而在未启用 LHA-Suspicion 的情况下,独立怀疑不会被重新传播,只有该成员自身的怀疑会被传播,最多发送 λlog(n) 次。

6.3.3 伙伴buddy系统

在SWIM协议中,被怀疑的成员无法保证第一时间获知怀疑信息。只有当节点收到关于自身的传播式怀疑消息时,才会知晓被怀疑状态。虽然gossip消息会搭载在故障检测消息(包括ping消息)上进行传输,但传播规则包含以下限制:每次搭载的gossip消息数量有限、每条gossip消息的重传次数受限,且会优先传播更新的gossip消息。

伙伴系统(Buddy System) 用一种优先通知被怀疑成员的机制取代了 SWIM 的附带消息选择器。这保证了任何向被怀疑节点发送 ping 消息的节点(无论是代表自身还是为其他节点的间接路径)都会在 ping 消息中传递怀疑信息。这可以促使反驳更早开始,即使没有 Lifeguard 的其他组件,这一机制也具有帮助。同时,它还能够使 本地健康感知探测(Local Health Aware Probe) 和 本地健康感知怀疑机制(Local Health Aware Suspicion) 更加高效地工作。

6.4. 评估

A. 评估标准

我们根据用于评估 SWIM 的相同标准来评估 Lifeguard(参见 [1] 的第 5 节)。具体来说:

  • 故障检测误报。Lifeguard 旨在减少被错误标记为故障的健康成员的数量。
  • 检测与传播延迟。Lifeguard 不应增加首次检测或完全传播真实故障检测所需的时间。
  • 消息负载。我们考虑发送的消息和字节数。Lifeguard 应该减少这些数量,或者不显著增加它们。

B. 测试的配置

第 ?? 节中描述的 Lifeguard 的三个组件分别单独评估并组合评估,以了解它们的相对贡献以及它们之间的相互作用方式。

Configuration Description
SWIM Regular SWIM
LHA-Probe SWIM + Local Health Aware Probe
LHA-Suspicion SWIM + Local Health Aware Suspicion
Buddy System SWIM + Buddy System
Lifeguard All Lifeguard components enabled

Table I Configurations tested.

针对表 1 中描述的每种配置都进行了实验。‘SWIM’ 配置表示完全禁用 Lifeguard 时的性能,它是其他组合进行比较的基准。这种做法是通过运行一个修改版的 Consul 实现的,其中每个组件都可以独立启用或禁用。

C. 怀疑超时配置

如第 IV-B 节所述,Lifeguard 的本地健康感知怀疑组件使用了最小和最大怀疑超时时间。在 memberlist 实现中,这些参数配置如下:

$$ \begin{align*} Min &= α\log_{10}(n)ProbeInterval\ Max &= βMin \end{align*} $$

其中:

  • n 是已知组中成员的数量。
  • ProbeInterval 是连续两次故障检测器探测消息之间的时间间隔。在所有实验中,默认值为 1 秒。
  • α 和 β 是可调参数。

为了研究可调参数的影响,每个实验重复进行了九次,完整启用了 Lifeguard(测试配置 Lifeguard),并使用了不同的 α 和 β 组合,其中 α = 2、4、5,β = 2、4、6。不同组合的性能与完全禁用 Lifeguard 的 memberlist 基线性能(测试配置 SWIM)进行了比较。后者具有固定的怀疑超时时间,相当于将 α 配置为 5,β 配置为 1。

D. 实验

SWIM 论文正确地指出,消息处理缓慢可能是由多种因素造成的,包括 CPU 耗尽、网络延迟以及本地主机或网络中的数据包丢失。最终结果总是无法及时处理一个或多个协议消息。为了本次研究的目的,我们通过在选定的组成员处暂停协议消息的发送和接收一段明确的时间来诱发消息处理缓慢。我们将每个成员处的延迟时段称为一次异常(anomaly)。

用于评估第五-A节(V-A)中定义的标准——阈值(Threshold)和区间(Interval)——的实验分为两种类型。这两种实验将在接下来的小节中进行描述。采用两种实验类型的原因如下:

  • 阈值实验(Threshold experiment)在每次实验中引入一组并发异常。这使得可以从异常开始到其被检测和传播的延迟得以考察,因为只有一组异常,因果关系清晰明了。
  • 然而,在现实世界中,CPU 和网络延迟可能是间歇性的,进程通常以小的突发形式取得进展。区间实验(Interval experiment)通过在每次实验的持续时间内以循环方式引入异常来探索这一领域。异常的持续时间及其间隔在多个不同的实验中被调整变化。

这些实验是通过部署 Consul(一种基于 memberlist 构建的服务发现和监控系统)来完成的。然而,Consul 的任何高层功能(例如基于 Raft[9] 的可用服务一致性视图)都没有被使用,并且集群部署时没有服务器实例,因此仅测试了 memberlist 的功能。

  1. 阈值实验:阈值实验用于评估 Lifeguard 对检测和传播延迟的影响。其形式如下:
  • 在单个 Linux 虚拟机中启动 128 个 Consul 代理,这些代理通过回环网络接口进行通信。
  • 允许代理在 15 秒内达到静止状态。
  • C 个实例(随机选择)进入异常状态,这些实例会在发送或接收来自集群中其他成员的任何协议消息之前或之后立即阻塞。
  • 这些异常状态持续一段时间 D,结束后被阻塞的发送和接收操作将解除阻塞。
  • 实验持续进行,直到所有 128 个 Consul 实例都重新将彼此视为健康状态,或者从实验开始起已经过了 120 秒为止。
Parameter Label Values Tested
Concurrent anomalies C 1, 4, 8, 12, 16, 20, 24, 28, 32
Duration of each anomaly D 128, 512, 2048, 8192, 16384, 32768

Table II THRESHOLD EXPERIMENT PARAMETERS AND VALUES TESTED. DURATIONS ARE GIVEN IN MILLISECONDS.

对于每个测试的配置,运行多次阈值实验,遍历 C 和 D 的一系列值。测试的值见表 II。对于 Lifeguard 组件的每种组合以及其他实验参数,该实验均运行 10 次。

  1. 区间实验:区间实验用于评估 Lifeguard 对误报故障检测以及消息负载的影响。它与阈值实验的形式相同,但有以下区别:
  • 在持续时间为 D 的异常周期结束时,每个异常的 Consul 实例会恢复正常运行一段时间 I。
  • 异常运行和正常运行的周期以 D 和 I 的时长分别为周期长度,循环往复,直到自测试开始至少经过 120 秒。测试在下一个异常周期结束时终止。
Parameter Label Values Tested
Concurrent anomalies C 1, 4, 8, 12, 16, 20, 24, 28, 32
Duration of each anomaly D 128, 512, 2048, 8192, 16384, 32768
Interval between anomalies I 1, 4, 16, 64, 256, 1024, 4096, 16384

Table III INTERVAL EXPERIMENT PARAMETERS AND VALUES TESTED. DURATIONS AND INTERVALS ARE GIVEN IN MILLISECONDS

对于每个测试的 Lifeguard 配置,运行多次区间实验,遍历 C、D 和 I 的一系列值。测试的值见表 III。对于 Lifeguard 组件的每种组合以及其他实验参数,该实验均运行 10 次。

E. 实验环境

实验运行在 Microsoft Azure 计算优化型(F 系列)虚拟机上,这些虚拟机部署在 2.4 GHz Intel Xeon E5-2673 v3(Haswell)处理器上。实验使用了 F16 实例,每个实例分配了 16 个核心和 32GiB 的内存。操作系统采用 Ubuntu 16.04 LTS 日构建版本 201701280,并且 Consul 被配置为将 DEBUG 级别的日志写入 /dev/shm(Ubuntu 默认配置的内存文件系统)。每次实验结束后,日志会被复制到固态硬盘(SSD)中。

为了减少调度和内存访问的不确定性,使用 Linux 的 numactl 工具将 8 个 Consul 代理固定到每个 CPU 核心及其关联的内存节点上。在实验的整个生命周期中,通过每秒采样一次 /proc/stat 来监控 CPU 使用情况,并确认所有实验中均有剩余的 CPU 容量,这表现为每个时间间隔内核心空闲时间的总和增加。实际上,对于 128 个 Consul 代理来说,这类 CPU 上的 16 个核心是过剩的。

F. 结果

这些实验探索了一个庞大的参数值组合空间。为了使结果更易于处理,我们首先在可调的 Suspicion 超时参数(在第 V-C 节中描述)设置为最高考虑值时,评估 Lifeguard 的性能:即 α = 5 和 β = 6。然后,我们进一步研究降低 α 和 β 值的影响。

  1. 故障检测误报:在第 V-D2 节中描述的区间实验(Interval experiment)用于衡量 Lifeguard 对故障检测误报发生率的影响,即健康代理被错误地标记为故障的情况。我们将故障检测误报定义为:每当某个 Consul 代理触发了一个故障事件,而该代理并不在被引入异常的代理集合中时,即发生一次误报。在这些误报中,我们进一步区分两种情况:发生在任何 Consul 代理上的误报(记作 FP),以及发生在健康代理上的误报(记作 FP-)。FP- 是最值得关注的,因为在这种情况中,涉及的两个代理——即触发事件的代理和事件所针对的代理——实际上都是健康的。
Configuration
Tested
FP
Events
FP-
Events
FP
% SWIM
FP-
% SWIM
SWIM 339002 1326 100.00 100.00
LHA-Probe 229574 436 67.72 32.88
Buddy System 318935 591 94.08 44.57
Lifeguard 5193 25 1.53 1.89

TABLE IV AGGREGATED FALSE POSITIVE RESULTS FOR ALL EXPERIMENTS WHERE α = 5 AND β = 6.

FOR EACH CONFIGURATION TESTED, FPEV E N T S IS THE TOTAL NUMBER OF FALSE POSITIVE EVENTS, AND FPEV E N T S IS THE NUMBER OF FALSE POSITIVE EVENTS AT HEALTHY NODES. FP % SWIM AND FP- % SWIM GIVE THE SAME RESULTS AS THE PERCENTAGE OF THEIR RESPECTIVE VALUES FOR SWIM.

表 IV 给出了所有在 α = 5 和 β = 6 的区间实验中汇总的误报统计信息。各列的含义如下:

  • 测试配置:启用的 Lifeguard 组件组合,如第 V-B 节所述。
  • FP 事件:所有 Consul 代理上发生的故障检测误报事件的总数。
  • FP- 事件:发生在健康代理上的故障检测误报事件的数量(即不在被引入异常的集合之外的代理)。
  • FP % SWIM:FP 事件占 SWIM(基线)值的百分比。
  • FP- % SWIM:FP- 事件占 SWIM(基线)值的百分比。

表 IV 表明,误报主要来自于发生在处理速度较慢的成员上的情况。这一点可以通过 FP- 在所有测试配置中(包括完全禁用 Lifeguard 的基线配置 SWIM)仅占 FP 的一小部分得到证明。

表 IV 还显示,通过引入 Lifeguard,误报率显著降低。Lifeguard 的所有组件都对这一减少做出了贡献,其中 本地健康感知怀疑机制(Local Health Aware Suspicion, LHA-Suspicion) 的单独贡献最大。结合所有组件(即 Lifeguard 整体)的效果最为显著。总的误报数(FP 事件)以及发生在健康节点上的误报数(FP- 事件)均降至 SWIM 基线水平的 2% 以下。这相当于误报减少了 50 倍以上。

伙伴系统(Buddy System)的效果值得注意,因为它使健康成员(FP-)上的误报减少了一半以上,但对整体误报数量(FP)的影响相对较小。这种差异可以通过其作用机制来解释——帮助被怀疑的成员更及时地意识到自己被怀疑的状态,从而可以更早开始反驳过程。健康的成员(与 FP- 相关)能够及时接收并处理这些反驳,而那些消息处理速度较慢的成员通常无法做到这一点。由于 FP 主要由处理消息缓慢的成员引起的误报主导,因此伙伴系统对其影响较小。

表 IV 汇总了所有测试的并发异常数量(C,如第 V-D1 节所定义)下的误报事件计数。图 2 和图 3 则分析了误报数量随并发异常数量变化的情况。

图 2 显示了每个测试的并发异常数量对应的总误报数(FP 事件)。它清楚地表明,误报数量随着并发异常数量的增加而上升,但在每个并发级别上,完整的 Lifeguard(Lifeguard)都能将误报数量减少 50 到 100 倍。

图 3 显示了每个测试的并发异常数量下,健康成员上的误报数(FP- 事件)。与图 2 相比,它的波动较大,因为这些事件的发生频率远低于总体误报。同样,误报数量随着并发异常数量的增加而上升,但在每个并发级别上,完整的 Lifeguard(Lifeguard)将健康成员上的误报数量减少了 10 到 100 倍。在完全启用 Lifeguard 的情况下,误报率被大幅降低,以至于在某些并发级别下,经过多次测试,健康节点上未发生任何误报。

  1. 检测与传播延迟:第 V-D1 节中描述的阈值实验(Threshold experiment)用于衡量 Lifeguard 对真实故障的检测延迟和传播延迟的影响。
Configuration
Tested
Median
1st Detect
99th %
1st Detect
99.9th %
1st Detect
Median
Full Dissem
99th %
Full Dissem
99.9th %
Full Dissem
SWIM 12.44 16.96 19.40 12.90 16.93 20.17
LHA-Probe 12.42 17.75 20.10 12.90 17.98 20.56
LHA-Suspicion 12.42 17.47 25.41 12.89 17.33 23.80
Buddy System 12.45 17.12 19.16 12.92 17.18 19.81
Lifeguard 12.45 17.90 21.20 12.91 18.05 21.68

TABLE V First detection and full dissemination latencies for all experiments where $\alpha = 5$ and $\beta = 6$.

All times are in seconds.

表 V 显示了不同 Lifeguard 组件在所有实验中对检测延迟和传播延迟的影响,其中 α = 5 且 β = 6。各列的含义如下:

  • Configuration Tested:启用的 Lifeguard 组件组合,如第 V-B 节中所定义。
  • Median 1st Detect:从异常开始到被其他代理首次检测到的中位时间。
  • 99th % 1st Detect : 从异常开始到被其他代理首次检测到的第 99 百分位时间。
  • 99.9th % 1st Detect : 从异常开始到被其他代理首次检测到的第 99.9 百分位时间。
  • Median Full Dissem : 从异常开始到将故障传播至所有健康代理的中位时间。
  • 99th % Full Dissem : 从异常开始到将故障传播至所有健康代理的第 99 百分位时间。
  • 99.9th % Full Dissem : 从异常开始到将故障传播至所有健康代理的第 99.9 百分位时间。

表 V 显示,完整的 Lifeguard(Lifeguard)将首次检测和完全传播的延迟略微提高。中位数延迟在首次检测和完全传播上均增加了 0.1 秒(不到 0.1%)。第 99 百分位和第 99.9 百分位的延迟增加幅度较大,首次检测延迟增加了约 1 秒(6-7%),而第 99.9 百分位延迟增加了 1.5-1.8 秒(7-9%)。本地健康感知探测机制(Local Health Aware Probe, LHA-Probe) 似乎对第 99 百分位延迟的增加贡献最大,而 本地健康感知怀疑机制(Local Health Aware Suspicion, LHA-Suspicion) 对第 99.9 百分位延迟的增加贡献最大。

  1. 消息负载:第 V-D2 节中描述的区间实验(Interval experiment)用于衡量 Lifeguard 对消息负载的影响。实验中发送的消息数量和总字节数通过 Consul 的遥测功能捕获。
Configuration
Tested
Msgs
Sent (M)
Bytes
Sent (GiB)
Msgs
% SWIM
Bytes
% SWIM
SWIM 435.33 149.15 100.00 100.00
LHA-Probe 428.62 134.28 98.46 90.03
LHA-Suspicion 484.55 158.87 111.31 106.52
Buddy System 435.62 147.67 100.07 99.01
Lifeguard 481.42 146.13 110.59 97.97

Table VI Aggregated message load results for all experiments where $\alpha = 5$ and $\beta = 6$.

对于每个Configuration Tested, Msgs Sent(消息发送量)是以百万为单位的发送的(复合)消息总数,Bytes Sent(字节发送量)是以吉字节(GiB)为单位的发送的总字节数。Msgs % SWIM 和 Bytes % SWIM 分别表示这些结果占 SWIM 基线对应值的百分比。

表 VI 给出了所有在 α = 5 和 β = 6 的实验中汇总的消息负载统计数据。各列的含义如下:

  • Configuration Tested : 启用的 Lifeguard 组件,如第 V-B 节中所定义。
  • Msgs Sent(M) : 所有 Consul 代理发送的(复合)SWIM 相关消息的总数,以百万为单位。通过将 gossip 消息附带在 ping 相关消息上形成的复合消息被计为一条消息。
  • Bytes Sent(GiB) : 发送消息的总大小,以吉字节(GiB)为单位。
  • Msgs % SWIM : 消息发送量(Msgs Sent)占 SWIM(基线)对应值的百分比。
  • Bytes % SWIM : 字节发送量(Bytes Sent)占 SWIM(基线)对应值的百分比。

表 VI 显示,对于 α = 5 和 β = 6 的实验,Lifeguard 导致发送的消息数量平均增加了约 11%,但发送的数据量实际上减少了约 2%。本地健康感知怀疑机制(Local Health Aware Suspicion, LHA-Suspicion) 是消息数量和字节发送量增加的主要贡献者。然而,这一影响被 本地健康感知探测机制(Local Health Aware Probe, LHA-Probe) 所抵消,后者减少了发送的消息数量和字节数。

  1. 怀疑超时调优:前几节的结果是在可调的怀疑超时参数设置为 α = 5 和 β = 6(这是考虑的最高值)的情况下获得的。我们现在来研究降低 α 和 β 值的影响。
$\alpha=2$
$\beta=2$
$\alpha=2$
$\beta=4$
$\alpha=2$
$\beta=6$
$\alpha=4$
$\beta=2$
$\alpha=4$
$\beta=4$
$\alpha=4$
$\beta=6$
$\alpha=5$
$\beta=2$
$\alpha=5$
$\beta=4$
$\alpha=5$
$\beta=6$
Med First
Med Full
53.14
55.12
54.10
56.28
54.34
56.74
82.96
84.42
83.04
84.03
83.12
84.42
99.76
99.92
99.52
99.61
100.08
100.08
99% First
99% Full
69.81
73.07
72.88
76.96
75.53
79.15
94.28
97.05
96.17
96.69
96.82
96.52
104.95
105.73
102.71
105.08
105.54
106.62
99.9% First
99.9% Full
76.08
76.20
75.41
75.11
80.36
78.58
99.07
92.17
93.71
95.14
94.69
92.71
112.32
107.64
111.44
107.93
109.28
107.49
FP
FP-
98.37
31.15
43.64
22.47
24.16
13.65
37.72
20.29
8.04
9.50
3.18
4.83
26.61
15.38
5.43
5.05
1.53
1.89

Table VII PERFORMANCE AS PERCENTAGE OF SWIM BASELINE WITH DIFFERENT TUNINGS OF α AND β.

每列显示了使用给定的 α 和 β 值配置 Lifeguard 时的指标。这些指标是第 V-F1 节和第 V-F2 节中定义的指标,并以 SWIM(SWIM)基线值的百分比形式展示。

表 VII 显示了在使用不同 α 和 β 组合配置 Lifeguard(Lifeguard)时,第 V-F1 节和第 V-F2 节中定义的指标值。这些指标以运行相同实验集时 SWIM(SWIM)的基线值的百分比形式展示。

观察到以下关系:

  • 所有六个延迟指标(中位首次检测时间、中位完全传播时间、99% 首次检测时间、99% 完全传播时间、99.9% 首次检测时间和 99.9% 完全传播时间)均与 α 呈正相关。
  • 当 α = 2 时,延迟指标(尤其是 99% 和 99.9% 的指标)也与 β 呈正相关。但在较高的 α 值下,这种相关性并不明显。
  • 总的误报数(FP)以及健康成员上的误报数(FP-)与 α 和 β 呈负相关。

因此,可以通过调整 α 和 β 来同时优化检测和传播延迟以及误报率。由于较低的 α 和 β 值会改善延迟,但会增加误报率,因此必须在降低检测延迟和提高误报率之间进行权衡。

然而,即使在最极端的权衡情况下(α = 2 且 β = 2),与 SWIM 相比,中位检测和传播延迟减少了约 45%,但健康节点上的误报率 FP- 仍然比 SWIM 的值降低了 68%(减少了 3 倍)。而在另一个极端(α = 5 且 β = 6)的情况下,中位延迟保持在 SWIM 的水平,但误报率降低了超过 98%(减少了 50 倍以上),同时第 99 百分位和第 99.9 百分位的延迟仅有适度增加。

在这些极端值之间选择 α 和 β 的值,可以对降低延迟和减少误报之间的权衡进行调整,尽管这种方式较为粗粒度。我们将 α 和 β 暴露为 Lifeguard 的参数。


专题:

本文发表于 2025-07-01,最后修改于 2025-07-01。

本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。


上一篇 « aio 下一篇 » pg的编译安装

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image