Oracle Quorum 简介
Oracle Quorum 的引入提高了由 2 个 ZooKeeper 实例组成的集群的可用性,该集群具有称为 Oracle 的故障检测器。当另一个实例被故障检测器 Oracle 识别为故障时,Oracle 旨在授予实例权限,该实例是 2 实例配置中唯一剩余的实例。
甲骨文的实施
每个实例都应访问一个包含 0 或 1 的文件,以指示该实例是否由 Oracle 授权。但是,由于故障检测器算法彼此不同,因此可以更改此设计。因此,可以覆盖 QuorumOracleMaj 中的 askOracle() 方法,以适应从Oracle 解密消息的首选方式。
部署上下文
Oracle 旨在提高 2 个 ZooKeeper 实例的集群的可用性;因此,投票成员的大小为2。换句话说,Oracle 解决了双实例集成中可能出现错误实例的共识问题。
在投票成员的大小超过 2 的情况下,使 Oracle 正常工作的预期方法是在识别到故障机器时重新配置集群的大小。例如配置5个实例,当故障机器断开与Leader的连接时,预计会有reconfig客户端向集群请求,使得集群重新形成为4个实例的配置。因此,一旦投票成员的大小等于 2,配置就属于 Oracle 旨在解决的问题域。
如何在zoo.cfg中部署 Oracle
不管集群的大小,oraclePath必须在初始化的时候配置好,和其他静态参数一样。下面显示了指定和启用 Oracle 的正确方法。
oraclePath=/to/some/file
zoo.cfg 的示例:
dataDir=/data
dataLogDir=/datalog
tickTime=2000
initLimit=5
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
oraclePath=/chassis/mastership
server.1=0.0.0.0:2888:3888;2181
server.2=hw1:2888:3888;2181
QuorumOracleMaj 旨在读取故障检测器的结果,该检测器写入文本文件,即 oracle 文件。
zoo.cfg 中的配置如下:
oraclePath=/to/some/file
假设你在 /some/path/result.txt 上写了故障检测器的结果,那么正确的配置如下:
oraclePath=/some/path/result.txt
那么,提供的文件的正确内容是什么?可以从终端使用以下命令创建示例文件:
$echo 1 > /some/path/result.txt
任何等效文件都适用于 QuorumOracleMaj 的当前实现。oracle 文件的数量应等于配置为启用 Oracle 的 ZooKeeper 实例的数量。换句话说,每个 ZooKeeper 实例都应该有它的 oracle 文件,并且这些文件不能共享;否则,将出现下一节中的问题。
启用 Oracle 部署后有何不同
QuorumPeerConfig在读取zoo.cfg包含oraclePath时将创建 QuorumOracleMaj 的实例,而不是默认的QuorumVerifier QuorumMaj。QuorumOracleMaj 继承自 QuorumMaj,并通过重写containsQuorum()方法与其超类不同。QuorumOracleMaj 旨在在领导者失去所有追随者并且无法维持法定人数时执行其版本的containsQuorum 。在其他情况下,QuorumOracleMaj应作为QuorumMaj执行。
Oracle应该注意什么
我们考虑一个由2 个ZooKeeper 实例和一个 Oracle组成的异步分布式系统。
活性问题:
当我们认为预言机满足 [CT] 引入的以下属性时:
Strong Completeness: There is a time after which every process that crashes is permanently suspected by every correct processes
系统的活跃性由 Oracle 保证。但是,当引入的预言机无法维护该属性时,预计会失去活性,如下例所示,
假设我们有一个 Leader 和一个 Follower,它们在广播状态下运行,系统会在以下情况下失去活力:
- Leader失败,但是Oracle没有检测到故障的Leader,这意味着Oracle不会授权Follower成为新的Leader。
- 当一个 Follower 失败,但 Oracle 没有检测到故障的 Follower 时,这意味着 Oracle 将授权 Leader 将系统向前移动。
安全问题:
失去进步
当系统在不同时间发生多个故障时,进度可能会丢失,如下例所示,
假设我们有一个 Leader(Ben) 和一个 Follower(John) 处于广播状态,
At T1 with zxid(0x1_1): L-Ben fails, and the F-John takes over the system under the authorization from the Oracle.
At T2 with zxid(0x2_1): The F-John becomes a new Leader, L-John, and starts a new epoch.
At T3 with zxid(0x2_A): L-John fails
At T4 with zxid(0x2_A): Ben recovers up and starts its leader election.
At T5 with zxid(0x3_1): Ben becomes the new leader, L-Ben, under the authorization from the Oracle.
在这种情况下,系统会在 L-Ben 失败后失去进度。
但是,可以通过使 Oracle 能够引用最新的 zxid 来防止丢失进度。当 Oracle 可以参考最新的 zxid 时,
At T5 with zxid(0x2_A): Ben will not end his leader election because the Oracle would not authorize although John is down.
然而,我们用生命来换取安全。
裂脑问题
我们认为 Oracle 满足 [CT] 引入的以下所需属性,
Accuracy: There is a time after which some correct processes is never suspected by any processes
然而,Oracle 给出的决定应该是互斥的。
换句话说,
假设我们有一个 Leader(Ben) 和一个 Follower(John) 处于广播状态,
- 在任何时候,即使故障检测器认为对方有故障,Oracle 也不会同时授权 Ben 和 John。或者
- 在任何时候,对于任意两个 Oracle 文件中的任意两个值,它们的值不都等于 1。
当 Oracle 在领导者选举阶段未能维护此属性时,预计会出现裂脑。
- 系统启动
- 失败的实例从失败中恢复。
实现故障检测器的概念示例
应该考虑故障检测器的结果是授权查询 ZooKeeper 实例是否有权向前移动系统,而无需等待故障检测器识别的故障实例。
硬件实现
假设两个专用硬件 hw1 和 hw2 可以分别托管 ZooKeeper 实例 zk1 和 zk2,并形成一个集群。硬件设备连接到这两个硬件,它能够确定硬件是否通电。所以,当hw1没有上电时,zk1无疑是有问题的。因此硬件设备将hw2上的oracle文件更新为1,说明zk1出现故障,授权zk2向前移动系统。
软件的实现
假设两个专用硬件 hw1 和 hw2 可以分别托管 ZooKeeper 实例 zk1 和 zk2,并形成一个集群。一个人可以在 hw1 和 hw2 上分别拥有另外两个服务 o1 和 o2。o1 和 o2 的工作是检测其他硬件是否存在。比如o1可以不断ping hw2来判断hw2是否上电。当o1无法ping通hw2时,o1识别出hw2故障,然后更新zk1的oracle文件为1,表示zk2故障,授权zk1向前移动系统。
使用 USB 设备作为 Oracle 来保持进度
在 macOS,10.15.7 (19H2) 中,外部存储设备挂载在/Volumes
. 因此,我们可以插入一个包含所需信息的 USB 设备作为预言机。当设备连接上时,oracle 授权领导者将系统向前移动,这也意味着其他实例失败。有六个步骤可以重现这种刺激。
- 首先,插入一个名为 的 USB 设备
Oracle
,然后我们可以预期它/Volumes/Oracle
是可以访问的。 - 其次,我们创建一个包含
1
在/Volumes/Oracle
命名下的文件mastership
。现在我们可以访问/Volumes/Oracle/mastership
了,zookeeper实例也可以访问了,看看它是否有权让系统向前移动。该文件可以通过以下命令轻松生成:$echo 1 > 掌握
-
第三,你应该有一个
zoo.cfg
类似下面的例子:dataDir=/data dataLogDir=/datalog tickTime=2000 initLimit=5 syncLimit=2 autopurge.snapRetainCount=3 autopurge.purgeInterval=0 maxClientCnxns=60 StandaloneEnabled=true admin.enableServer=true oraclePath=/Volumes/Oracle/mastership server.1= 0.0.0.0:2888:3888;2181 服务器.2=hw1:2888:3888;2181
(注意)不会发生脑裂问题,因为在这种刺激中只有一个 USB 设备。 此外,mastership
不应由多个实例共享。 因此,只有一个 ZooKeeper 实例配置了 Oracle。 有关更多信息,请参阅安全问题部分。
- 第四,启动集群,预计正常形成quorum。
- 第五,在不连接到 USB 设备或
mastership
包含 0 的情况下终止实例。有两种情况可以预期:
- 发生leader故障,剩下的实例由于oracle的原因自行完成了leader选举。
- 由于预言机,法定人数仍然保持不变。
- 最后,当 USB 设备被移除时,它
/Volumes/Oracle/mastership
变得不可用。因此,按照目前的实现,每当Leader查询oracle时,oracle都会抛出异常并返回FALSE
。重复第五步,然后预计系统要么无法从领导者故障中恢复,要么领导者失去仲裁。在任何一种情况下,服务都会中断。
通过这些步骤,我们可以轻松地展示和练习预言机如何与双实例系统一起工作。
参考
[CT] Tushar Deepak Chandra and Sam Toueg. 1991. Unreliable failure detectors for asynchronous systems (preliminary version). In Proceedings of the tenth annual ACM symposium on Principles of distributed computing (PODC '91). Association for Computing Machinery, New York, NY, USA, 325–340. DOI:https://doi.org/10.1145/112600.112627