ZooKeeper Internals -- ZooKeeper内部工做方式

ZooKeeper Internals
介绍
原子广播
保证,属性和定义
领导者激活
活动消息
摘要
比较
法定人数
记录
开发者指南
记录在正确的级别
使用标准的slf4j成语git

介绍
本文档包含有关ZooKeeper内部工做方式的信息。到目前为止,它讨论了如下主题:github

原子广播
记录算法

原子广播
ZooKeeper的核心是一个原子消息系统,可使全部服务器保持同步。apache

保证,属性和定义
ZooKeeper使用的消息传递系统提供的特定保证以下:api

可靠的交付:若是消息m由一台服务器提供,它将最终由全部服务器提供。数组

总订单:若是一条服务器在消息b以前传递消息,则全部服务器将在b以前传送消息。若是a和b是传递的消息,则a将在b以前传递或b将在传递以前传递。服务器

因果顺序:若是在b的发送者发送消息a以后发送消息b,则必须在b以前订购。若是发送方在发送b后发送c,则必须在b以后订购c。markdown

ZooKeeper消息传递系统还须要高效,可靠,易于实施和维护。咱们大量使用消息传递,所以咱们须要系统可以每秒处理数千个请求。虽然咱们能够要求至少k + 1个正确的服务器来发送新消息,但咱们必须可以从相关故障中恢复,例如断电。当咱们实施该系统时,咱们几乎没有时间和不多的工程资源,所以咱们须要一个工程师能够访问的协议,而且易于实现。咱们发现咱们的协议知足了全部这些目标。app

咱们的协议假设咱们能够在服务器之间构建点对点FIFO通道。虽然相似的服务一般假设消息传递可能丢失或从新排序消息,但咱们假设FIFO通道很是实用,由于咱们使用TCP进行通讯。具体来讲,咱们依赖TCP的如下属性:异步

有序传递:数据按照发送的顺序传送,只有在传送完m以前发送的全部邮件以后才传送消息m。(这样作的必然结果是,若是消息m丢失,m以后的全部消息都将丢失。)

关闭后没有消息:一旦FIFO通道关闭,将不会收到任何消息。

FLP证实,若是可能出现故障,则没法在异步分布式系统中实现共识。为确保咱们在出现故障时达成共识,咱们会使用超时。可是,咱们依靠活力时间而不是正确性。所以,若是超时中止工做(例如时钟故障),则消息传递系统可能会挂起,但不会违反其保证。

在描述ZooKeeper消息传递协议时,咱们将讨论数据包,提议和消息:

数据包:经过FIFO通道发送的字节序列

提案:协议单位。经过与法定数量的ZooKeeper服务器交换数据包来商定提案。大多数提案都包含消息,但NEW_LEADER提案是与消息不对应的提案示例。

消息:要以原子方式广播到全部ZooKeeper服务器的字节序列。在提交以前提交并赞成的消息。

如上所述,ZooKeeper保证了消息的总顺序,而且它还保证了提议的总顺序。ZooKeeper使用ZooKeeper事务id(zxid)公开总排序。全部提案在提议时都会加盖zxid,并准确反映总排序。提案将发送到全部ZooKeeper服务器,并在法定人数确认提案时提交。若是提案包含消息,则在提交提案时将传递消息。确认意味着服务器已将提议记录到持久存储。咱们的法定人数要求任何一对仲裁必须至少有一个共同的服务器。咱们经过要求全部法定人数的大小(n / 2 + 1)来确保这一点)其中n是组成ZooKeeper服务的服务器数量。

zxid有两个部分:纪元和计数器。在咱们的实现中,zxid是一个64位数字。咱们使用高阶32位用于纪元,低阶32位用于计数器。由于它有两个部分表明zxid既是数字又是一对整数,(epoch,count)。时代数字表明了领导层的变化。每当新领导人上台时,它将拥有本身的纪元号码。咱们有一个简单的算法来为一个提议分配一个惟一的zxid:领导者只需递增zxid以得到每一个提案的惟一zxid。领导激活将确保只有一个领导者使用给定的纪元,所以咱们的简单算法保证每一个提案都具备惟一的ID。

ZooKeeper消息传递包含两个阶段:

领导者激活:在此阶段,领导者创建正确的系统状态,并准备开始提出建议。

主动消息传递:在此阶段,领导者接受建议和协调消息传递的消息。

ZooKeeper是一个总体协议。咱们不关注个别提案,而是关注整个提案流。咱们严格的订购使咱们可以有效地完成这项工做并大大简化咱们的协议。领导激活体现了这种总体概念。领导者只有在达到法定数量的粉丝时才会变得活跃(领导者也算做跟随者。你老是能够为本身投票)与领导者同步,他们拥有相同的状态。该州包括领导者认为已经提交的全部提案以及跟随领导者的提议,NEW_LEADER提案。(但愿你是在想本身,领导者认为已提交的提案包括全部真正提交的提案吗?答案是确定的。下面,咱们说明缘由。)

领导者激活
领导者激活包括领导者选举。咱们目前在ZooKeeper中有两个领导者选举算法:LeaderElection和FastLeaderElection(AuthFastLeaderElection是FastLeaderElection的变体,它使用UDP并容许服务器执行简单形式的身份验证以免IP欺骗)。只要符合如下条件,ZooKeeper消息传递并不关心选择领导者的确切方法:

领导者已经看到了全部粉丝中最高的zxid。
法定数量的服务器已承诺跟随领导者。
在这两个要求中,只有第一个,跟随者须要保持正确操做的最高zxid。第二个要求,即法定数量的追随者,只须要很高的几率。咱们将从新检查第二个要求,所以若是在领导者选举期间或以后发生失败而且法定人数丢失,咱们将经过放弃领导者激活和进行另外一次选举来恢复。

领导者选举后,单个服务器将被指定为领导者,并开始等待粉丝链接。其他服务器将尝试链接到领导者。领导者将经过发送他们遗失的任何提案与追随者同步,或者若是追随者缺乏太多提案,它将向关注者发送状态的完整快照。

有一个角落案例,其中有一个跟随者有提议,U,领导人看不到。提案按顺序排列,所以U的提案的zxids高于领导者看到的zxids。追随者必须在领导人选举后到达,不然追随者将被选为领导者,由于它已经看到更高的zxid。因为提交的提案必须由法定数量的服务器看到,而且选出领导者的法定数量的服务器没有看到U,所以您的提议还没有提交,所以能够将其丢弃。当追随者链接到领导者时,领导者将告诉追随者丢弃U.

一个新的领导者创建一个zxid​​来开始使用新的提议,经过得到它所见过的最高zxid的时代e,并设置下一个zxid​​用于(e + 1,0),领导者与跟随者同步后,它将提出一个NEW_LEADER提案。提交NEW_LEADER提案后,领导者将激活并开始接收和发布提案。

这听起来很复杂,但这里是领导者激活过程当中的基本操做规则:

跟随者将在与领导者同步后确认NEW_LEADER提案。
跟随者只会使用来自单个服务器的给定zxid确认NEW_LEADER提议。
当法定​​数量的粉丝确认后,新的领导者将提交NEW_LEADER提案。
当NEW_LEADER提议为COMMIT时,关注者将提交从领导者收到的任何州。
在NEW_LEADER提案得到COMMITED以前,新领导者不会接受新提案。
若是领导者选举错误地终止,咱们就没有问题,由于因为领导者没有法定人数,所以不会提交NEW_LEADER提案。当发生这种状况时,领导者和任何剩下的追随者将超时并返回领导者选举。

活动消息
领导者激活完成全部繁重的工做。一旦领导者加冕,他就能够开始提出提案。只要他仍然是领导者,就不可能出现其余领导者,由于没有其余领导者可以得到法定数量的粉丝。若是新的领导者确实出现,那就意味着领导者失去了法定人数,新的领导者将清理领导激活期间留下的任何混乱。

ZooKeeper消息传递的操做相似于传统的两阶段提交。

全部通讯通道都是FIFO,因此一切都按顺序完成。具体而言,遵照如下操做约束:

领导者使用相同的订单向全部粉丝发送提案。此外,此顺序遵循收到请求的顺序。由于咱们使用FIFO通道,这意味着关注者也会按顺序接收提案。
关注者按照收到的顺序处理消息。这意味着将按顺序确认消息,而且因为FIFO通道,领导者将按顺序接收来自关注者的ACK。这也意味着若是消息$ m $已写入非易失性存储器,则在$ m $以前建议的全部消息都已写入非易失性存储器。
一旦有法定数量的粉丝确认消息,领导者将向全部粉丝发出COMMIT。因为消息按顺序被确认,所以将由跟随者按顺序接收的领导者发送COMMIT。
COMMIT按顺序处理。在提交提案时,关注者会发送提案消息。

摘要
你去吧 它为何有效?具体而言,为何新领导人认为的一系列提案老是包含任何实际提交的提案?首先,全部提议都有一个惟一的zxid,所以与其余协议不一样,咱们永远没必要担忧为同一个zxid​​提出两个不一样的值; 粉丝(领导者也是粉丝)按顺序查看和记录提案; 提案按顺序提交; 因为粉丝一次只跟随一位领导者,因此每次只有一位活跃的领导者; 一位新领导人已经看到了上一个时代的全部承诺提案,由于它已经从法定数量的服务器中看到了最高的zxid; 新领导人看到的上一个时代的任何未提出的提议都将由该领导者在活跃以前承诺。

比较
这不仅是Multi-Paxos吗?不,Multi-Paxos须要一些方法来确保只有一个协调员。咱们不期望这种保证。相反,咱们使用领导者激活来恢复领导层变革,或者让老领导人相信他们仍然活跃。

这不只仅是Paxos吗?您的活动消息传递阶段看起来就像Paxos的第2阶段?实际上,对咱们来讲,主动消息传递看起来就像是2阶段提交,而不须要处理停止。主动消息传递与它们具备交叉提议排序要求的意义不一样。若是咱们不对全部数据包保持严格的FIFO排序,它就会崩溃。此外,咱们的领导者激活阶段与他们两个都不一样。特别是,咱们对epochs的使用容许咱们跳过未提交的提议块,而不用担忧给定zxid的重复提议。

法定人数
原子广播和领导者选举使用法定人数的概念来保​​证系统的一致视图。默认状况下,ZooKeeper使用多数仲裁,这意味着在其中一个协议中发生的每一个投票都须要多数投票。一个例子是认可领导者提案:领导者只有在收到法定数量的服务器的确认后才能提交。

若是咱们从使用多数性中提取咱们真正须要的属性,咱们只须要保证用于经过投票验证操做的进程组(例如,确认领导者提议)在至少一个服务器中成对交叉。使用多数人保证这样的财产。可是,还有其余方法能够构建与多数群体不一样的法定人数。例如,咱们能够为服务器的投票分配权重,并说一些服务器的投票更重要。为了得到法定人数,咱们获得足够的票数,以便全部投票的权重总和大于全部权重总和的一半。

使用权重而且在广域部署(共址)中有用的不一样结构是分层结构。经过这种构造,咱们将服务器分红不相交的组并为进程分配权重。为了造成法定人数,咱们必须从大多数G组得到足够的服务器,这样对于G中的每一个组g,来自g的投票总和大于g中权重总和的一半。有趣的是,这种结构能够实现更小的法定人数。例如,若是咱们有9个服务器,咱们将它们分红3组,并为每一个服务器分配1的权重,而后咱们就能够造成大小为4的仲裁。注意,两个进程的子集各占大多数来自大多数组中的每一个组的服务器必然具备非空交叉点。

使用ZooKeeper,咱们为用户提供配置服务器以使用多数仲裁,权重或组层次结构的功能。

记录
Zookeeper使用slf4j做为日志记录的抽象层。如今选择版本1.2中的log4j做为最终的日志记录实现。为了更好地嵌入支持,计划未来决定为最终用户选择最终的日志记录实现。所以,始终使用slf4j api在代码中编写日志语句,但配置log4j以了解如何在运行时进行日志记录。请注意,slf4j没有FATAL级别,FATAL级别的旧消息已移至ERROR级别。有关为ZooKeeper配置log4j的信息,请参阅ZooKeeper管理员指南的“ 日志记录”部分。

开发者指南
在代码中建立日志语句时,请遵循 slf4j手册。 在建立日志语句时,请阅读有关性能的常见问题解答。补丁审阅者将查找如下内容:

记录在正确的级别

slf4j中有多个级别的日志记录。

选择正确的一个很重要。按从高到低的顺序:

ERROR级别指定可能仍容许应用程序继续运行的错误事件。
WARN级别表示潜在的有害状况。
INFO级别指定信息性消息,以粗粒度级别突出显示应用程序的进度。
DEBUG Level指定对调试应用程序最有用的细粒度信息事件。
TRACE Level指定比DEBUG更细粒度的信息事件。
ZooKeeper一般在生产中运行,以便将INFO级别严重性和更高(更严重)的日志消息输出到日志。

使用标准的slf4j成语

静态消息记录

LOG.debug("process completed successfully!");
可是,当须要建立参数化消息时,请使用格式化锚点。

LOG.debug("got {} messages in {} minutes",new Object[]{count,time});
命名

记录器应以其使用的类命名。

public class Foo {

private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
....
public Foo() {
    LOG.info("constructing Foo");

异常处理

try {

// code

} catch (XYZException e) {

// do this
LOG.error("Something bad happened", e);
// don't do this (generally)
// LOG.error(e);
// why? because "don't do" case hides the stack trace

// continue process here as you need... recover or (re)throw

}

转载来源:https://github.com/apache/zoo...

相关文章
相关标签/搜索