ScyllaDB的故障探测器原理与实现



  • ScyllaDB 是一个兼容Cassandra协议的NoSQL分布式数据库,以高性能低延迟著称。关于该数据库的更多介绍,请阅读其官网。

    分布式系统中为什么需要故障探测机制?

    在分布式系统中,故障探测器(Failure Detector)是一个重要的子系统,用于监控与判断集群中其他节点是否存活。ScyllaDB 集群中每个节点利用GOSSIP协议获取关于集群中其他节点的状态信息。当节点失效后,故障探测器将这一信息告诉其他子系统。其他子系统采取相应的措施,例如避免将读请求发送到失效节点上,启用Hinted Handoff机制。

    注,本文分析的ScyllaDB代码为branch-1.7。

    ScyllaDB故障探测的原理

    在ScyllaDB中,故障探测器是基于论文The Phi Accrual Failure Detector实现的。该论文主要提出了一种称为Accrual Failure Detector的方案。简单地说, 与传统故障探测器不同。传统故障探测器输出不是被监控节点关于时间的一个分段函数,函数值为BOOLEN值。而Accrual Failure Detector 输出监控节点关于时间的连续函数(下文记该函数为Phi(t)),气值为浮点数值。该函数具备如下性质:

    • 如果被监控节点已经失效,Accrual Failure Detector输出值随着时间趋近于无穷大。
    • 如果被监控节点已经失效,存在一个时间段,此时间段后,Accrual Failure Detector输出值是单调递增的。
    • 如果被监控节点是活的(alive),Accrual Failure Detector输出值存在上限。
    • 如果被监控节点是活的,存在一个时间段,此时间段后,Accrual Failure Detector输出值值为0。

    很明显,Accrual Failure Detector 的输出值,可以作为判断节点存活与否的依据。

    Accrual Failure Detector怎样定义函数Phi(t)呢? 该函数定义,需要满足上述性质。该函数定义如下:

    0_1501381446510_fd14d16b-8563-48f9-8ad5-343fa2e39933-image.png

    其中T{last}表示最近一次收到该节点信息的时间戳,T{now}为当前时间戳,P_later(T) 是一个概率,表示从上一次收到节点信息开始,超过T时间间隔再次收到该节点信息的概率。这个Phi(t)的定义显然满足上述4个性质,为了方便理解,我截取了原文的一张图(为了方便说吗,我在图中用红色字标注了序号):

    0_1501381461963_165f5641-cb1c-41c5-a3df-38e3242d300d-image.png
    在(2)中维护了一个采样的队列,它的均值和方差确定了(3)中的概率密度曲线的样子,从而也确定了P_later(T)的输出,也确定了(4)中输出Phi(T)的值。通过观察(4)中定义的函数曲线可知,当某个节点失效后,在过去一段时间内再次受到心跳的概率越来越趋近于0, Phi(T)将趋近于无穷大。

    ScyllaDB的故障探测的实现

    在ScyllaDB中,在failure_detector.{hh, cc} 文件中定义了failure_detector类,实现了故障探测器的全部逻辑。

    在节点Node{i} 中,failure_detector 类为集群中其他每一个节点Node{j}维护了一个固定尺寸的队列,该队列记录了该节点Node{i}获得Node{j}节点状态信息的时间间隔。

    节点Node{i} 与节点Node{i} 之间按照GOSSIP协议传递状态信息,他们通过类似TCP/IP协议的三次握手机制,交换彼此的形象,并通过Version来更新自身信息(如果自己Version比对方小)。需要注意的是,节点Node{i} 可能并不是每一次都会直接与某个节点Node{k}交互,它可以通过另外其他节点交互的信息中,获取关于Node{k}的信息,从而为节点Node{k} 也记录了时间间隔。

    和基于心跳来实现故障探测一个显著不同是,集群节点必须和集群其他节点周期性发送心跳。考虑一种情况,节点Node{i} 与节点Node{j} 之间网络故障,心跳全部超时。但节点Node{i} 与节点Node{k} 之间心跳正常,节点Node{k} 与节点Node{j} 之间心跳正常。节点Node{i} 没有全部利用这些信息,从而判定节点Node{j} 失效。

    故障探测器类会周期性地将系统中节点的Phi(t)值告诉其他子系统(其他感兴趣的子系统会定阅Phi(t)的输出,也就是设置了回调函数,该函数周期性地被调用,参数包含F(t))。各个子系统根据自己的逻辑,对节点做出不同的操作。例如,可以将Phi(t)的值与系统预设的阈值比较,当超过阈值,就认为节点失效。

    另外,由于ScyllaDB在几乎在每个CPU上执行了几乎相同的逻辑。这里用几乎,就表明有例外,故障探测和GOSSIP协议是只运行在CPU 0 上的,其他CPU 会复制CPU 0的结果。这样整个进程中,将看到一致的状态。

    总结

    ScyllaDB使用GOSSIP协议,快速传播节点的状态信息,使得每个节点都能通过不同渠道去了解其他节点状态。在这个过程中,能够记录获取感知都其他节点存在信息的时间间隔,按照Accrual Failure Detector 模型,为每一个节点计算Phi(T)。其他子系统对节点状态感兴趣,则会订阅该值,系统会周期性让其他子系统去解释这个Phi(T)值。该实现比单纯基于心跳协议来实现,更鲁棒,更灵活。

    [下一篇,将介绍ScyllaDB的GOSSIP协议实现]

    EOF


登录后回复
 

与 区块链大学 | qkldx.net 的连接断开,我们正在尝试重连,请耐心等待