Oracle 法定人数简介
Oracle 法定人数简介提高了 2 个 ZooKeeper 实例集群的可用性,其中故障检测器称为 Oracle。Oracle 被设计为在故障检测器(即 Oracle)将另一个实例识别为有故障时,向 2 实例配置中唯一剩余的实例授予权限。
Oracle 的实现
每个实例都应访问一个文件,其中包含 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 读取包含 oraclePath 的 zoo.cfg 时,它将创建一个 QuorumOracleMaj 实例,而不是默认的 QuorumVerifier QuorumMaj。QuorumOracleMaj 继承自 QuorumMaj,并且通过覆盖 containsQuorum() 方法而与其超类不同。QuorumOracleMaj 被设计为在 Leader 失去所有 Follower 且无法维持法定人数时执行其版本的 containsQuorum。在其他情况下,QuorumOracleMaj 将作为 QuorumMaj 执行。
我们应该注意 Oracle 的是什么
我们考虑一个异步分布式系统,该系统由 2 个 ZooKeeper 实例和一个 Oracle 组成。
活性问题
当我们考虑 Oracle 满足 [CT] 引入的以下属性时
Strong Completeness: There is a time after which every process that crashes is permanently suspected by every correct processes
Oracle 确保了系统的活性。但是,当引入的 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。当设备连接时,oracle 授权领导者推进系统,这也意味着另一个实例失败。有六个步骤来重现此模拟。
- 首先,插入一个名为
Oracle
的 USB 设备,然后我们可以预期可以访问/Volumes/Oracle
。 - 其次,我们在
/Volumes/Oracle
下创建一个包含1
的文件,名为mastership
。现在我们可以访问/Volumes/Oracle/mastership
,zookeeper 实例也可以访问它,以查看它是否有权推动系统向前发展。可以通过以下命令轻松生成该文件$echo 1 > mastership
-
第三,您应该有一个类似于以下示例的
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 server.2=hw1:2888:3888;2181
(注意)由于此仿真中只有一个 USB 设备,因此不会出现脑裂问题。 此外,mastership
不应由多个实例共享。 因此,只有一个 ZooKeeper 实例配置了 Oracle。 有关更多信息,请参阅安全问题部分。
- 第四,启动集群,并预期它正常形成法定人数。
- 第五,终止实例,既不连接到 USB 设备,也不包含 0 的
mastership
。有两种情况可以预期
- 由于 oracle,发生领导者故障,剩余的实例自行完成领导者选举。
- 由于 oracle,法定人数仍然保持。
- 最后,当移除 USB 设备时,
/Volumes/Oracle/mastership
变得不可用。因此,根据当前实现,每当领导者查询 oracle 时,oracle 都会抛出异常并返回FALSE
。重复第五步,然后预期系统无法从领导者故障中恢复,或者领导者失去法定人数。在这两种情况下,服务都会中断。
通过这些步骤,我们可以轻松展示和演练 oracle 如何与双实例系统配合使用。
参考
[CT] Tushar Deepak Chandra 和 Sam Toueg。1991 年。异步系统的不可靠故障检测器(初步版本)。在分布式计算原理第十届年度 ACM 研讨会论文集(PODC '91)中。美国纽约州纽约市计算机协会,325-340。DOI:https://doi.org/10.1145/112600.112627