Zookeeper & Etcd 关键对比汇总

Zookeeper & Etcd 关键对比汇总

1. 选举

1.1 Etcd选举过程

  1. 每个节点启动时作为Follwer角色,经过一个ElectionTimeout(启动时随时生成的 默认150ms~300ms)后进入Candidate状态,并向其他节点发送消息MsgVote消息(本节点的Term、最新的日志Index,最新日志的LogTerm);

  2. 其他节点收到投标后,进行两步判断:

    1. 预判断,:
      1. r.Vote==m.Form(是否已经投标给该节点)
      2. 当前节点未投标且无leader节点;
      3. 对于预投标(PreVote),消息的任期比当前节点大;
    2. 任何一个条件判断则可以进入正式判断
      1. 若消息中日志Term 对当前节点大则投给消息发送方,若日志Term相等,Index比当前节点大则也将票投给消息发送方r.raftLog.isUpToDate(m.Index, m.LogTerm)
    3. 若上面的判断失败,则返回拒绝
  3. 当发起投标方收到半数以上的头条,则转换自己的角色becomeLader (raft.go/stepCandidate),并广播一条空消息给其他所有节点。

    • Candidate –> Leader
    • Candidate –> Follower

    广播时间 << 选举超时时间 << 平均故障间隔时间

1.2 Zookeeper选举过程

双向心跳,Leader收不到半数以上的Follower节点发来的心跳则发起选举;Follower未收到Leader心跳则发起选举,选举时状态切换成LOOKING

  1. 投票节点:首先将节点状态转变成LOOKING状态;

  2. 投票节点:将自己作为(SID、ZXID)广播给其他节点;

  3. 其他节点:接收其他节点的选举投票;

  4. 其他节点:如果接收的选举纪元大于本地的选举轮数则更新本地选举轮数,并将接收的投票跟本地投票比较后更新本地投票,再广播出去。否则执行4

  5. 其他节点:判断外部投票是否优于本地,如果是则更新本地提议再广播出去;

    1. 比较纪元大小(最新日志记录的纪元,并不是当前选举纪元);

    2. 纪元相等的情况,比较 Zxid

    3. Zxid相等的情况下 则比较 Sid

      return ((newEpoch > curEpoch) ||

      ((newEpoch == curEpoch) &&
      ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
  6. 投票节点:收集投票提议,并进行统计,若有节点收集的投票大于一半以上则认为该节点为Leader节点;

  7. 投票节点:投票选定节点是该节点则转变自己的服务器状态为LEADING,若不是则转换成FOLLOWING | OBSERVING

  • LOOKING –> LEADING
  • LOOKING –> FOLLOWING

2. 数据同步

2.1 Zookeeper数据同步

数据同步分为四种DIFFTRUNCTRUNC+DIFFSNAP
Learner节点最后处理的ZXIDpeerLastZxidLeader服务器提议缓存队列commitedLog最小最大ZXIDminCommitedLogmaxCommitedLog

  1. peerLastZxid小于minCommitedLog,则执行SNAP
  2. peerLastZxidminCommitedLogmaxCommitedLog之间,则执行DIFF
  3. peerLastZxid大于 maxCommitedLog 时,则执行TRUNC
  4. peerLastZxidminCommitedLogmaxCommitedLog之间但又不在leader中,则TRUNC+DIFF

2.2 Etcd数据同步

EtcdLeader为每个从节点保存两个信息nextIndex(下一次要复制的位置)、matchIndex(已经匹配的位置)

  1. 当从Candidate角色转换成Leader角色后,会将所有节点的nextIndex改为当前节点最大的IndexmatchIndex设置为 0,向所有节点发送Append Entries信息;

    1. 若接收到信息的节点的commitedId大于发送过来的nextIndex,则告诉Leader该索引号;

    2. msg.Index&Term在节点日志中,则告知Leader节点目前的mLastIndex

    3. 若不匹配,则发送拒绝,并返回其日志中的LastIndex

      若节点信息对不上nextIndex则返回日志失败,Leader不断前移nextIndex进行循环往复尝试,直至节点追加成功;

3. Watch机制

3.1 Zookeeper

Zookeeperwatch由引用层的WatchManager进行管理,其中包含NodeWatcherChildWatcher

  1. 当有数据更新时同时会调用WatchManager进行触发事件;
  2. WatcherManager中筛选出符合条件的Watcher触发下发,并把映射给摘除。
    1. 只会触发一次,且不会把真正的数据下发下去而只下发通知状态、事件类型、节点路径,具体变更数据由Client自行来查询;
    2. Watcher事件传递到Client触发后也会删除映射关系。

因此存在丢失的可能:

  • 在对于同一个key或者同一范围的监听,若修改速度迅速,会漏事件

3.2 Etcd

Etcd由两个协程和两个WatcherGroup来管理Watcher事件。详细见:Etcd Watch机制

  1. 带版本号监听;
  2. 连续监听不是单次监听。

4. 线性一致性

zab&Raft 只是共识算法,并非线性一致性,实现线性一致性需要上层做出努力。如etcd的线性一致性读。

什么是线性一致性读

对于同一对象,读写请求按顺序线性进行。(读到最新写的数据

4.1 引用

5. 参考材料

您的支持是我创作源源不断的动力