Zookeeper & Etcd 关键对比汇总
1. 选举
1.1 Etcd选举过程
- 每个节点启动时作为 - Follwer角色,经过一个- ElectionTimeout(启动时随时生成的- 默认150ms~300ms)后进入- Candidate状态,并向其他节点发送消息- MsgVote消息(本节点的- Term、最新的日志- Index,最新日志的- LogTerm);
- 其他节点收到投标后,进行两步判断: - 预判断,: - r.Vote==m.Form(是否已经投标给该节点)
- 当前节点未投标且无leader节点;
- 对于预投标(PreVote),消息的任期比当前节点大;
 
- 任何一个条件判断则可以进入正式判断:- 若消息中日志Term对当前节点大则投给消息发送方,若日志Term相等,Index比当前节点大则也将票投给消息发送方r.raftLog.isUpToDate(m.Index, m.LogTerm)
 
- 若消息中
- 若上面的判断失败,则返回拒绝
 
- 预判断,: 
- 当发起投标方收到半数以上的头条,则转换自己的角色 - becomeLader(- raft.go/stepCandidate),并广播一条空消息给其他所有节点。- Candidate –> Leader
- Candidate –> Follower
 - 广播时间 << 选举超时时间 << 平均故障间隔时间
1.2 Zookeeper选举过程
双向心跳,Leader收不到半数以上的Follower节点发来的心跳则发起选举;Follower未收到Leader心跳则发起选举,选举时状态切换成LOOKING。
- 投票节点:首先将节点状态转变成 - LOOKING状态;
- 投票节点:将自己作为 - (SID、ZXID)广播给其他节点;
- 其他节点:接收其他节点的选举投票; 
- 其他节点:如果接收的选举纪元大于本地的选举轮数则更新本地选举轮数,并将接收的投票跟本地投票比较后更新本地投票,再广播出去。否则执行4; 
- 其他节点:判断外部投票是否优于本地,如果是则更新本地提议再广播出去; - 比较纪元大小(最新日志记录的纪元,并不是当前选举纪元); 
- 纪元相等的情况,比较 - Zxid;
- Zxid相等的情况下 则比较- Sid;- return ((newEpoch > curEpoch) || - ((newEpoch == curEpoch) && ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
 
- 投票节点:收集投票提议,并进行统计,若有节点收集的投票大于一半以上则认为该节点为 - Leader节点;
- 投票节点: 若投票选定节点是该节点则转变自己的服务器状态为 - LEADING,若不是则转换成- FOLLOWING | OBSERVING。
- LOOKING –> LEADING
- LOOKING –> FOLLOWING
2. 数据同步
2.1 Zookeeper数据同步
数据同步分为四种DIFF、TRUNC、TRUNC+DIFF、SNAP
设 Learner节点最后处理的ZXID为peerLastZxid,Leader服务器提议缓存队列commitedLog最小最大ZXID为minCommitedLog、maxCommitedLog;
- 若peerLastZxid小于minCommitedLog,则执行SNAP;
- 若peerLastZxid在minCommitedLog与maxCommitedLog之间,则执行DIFF;
- 若peerLastZxid大于maxCommitedLog时,则执行TRUNC;
- 若peerLastZxid在minCommitedLog与maxCommitedLog之间但又不在leader中,则TRUNC+DIFF。
2.2 Etcd数据同步
Etcd 中Leader为每个从节点保存两个信息nextIndex(下一次要复制的位置)、matchIndex(已经匹配的位置)
- 当从 - Candidate角色转换成- Leader角色后,会将所有节点的- nextIndex改为当前节点最大的- Index,- matchIndex设置为 0,向所有节点发送- Append Entries信息;- 若接收到信息的节点的 - commitedId大于发送过来的- nextIndex,则告诉- Leader该索引号;
- 若 - msg.Index&Term在节点日志中,则告知- Leader节点目前的- mLastIndex;
- 若不匹配,则发送拒绝,并返回其日志中的 - LastIndex。- 若节点信息对不上 - nextIndex则返回日志失败,- Leader不断前移- nextIndex进行循环往复尝试,直至节点追加成功;
 
3. Watch机制
3.1 Zookeeper
Zookeeper的watch由引用层的WatchManager进行管理,其中包含NodeWatcher和ChildWatcher:
- 当有数据更新时同时会调用WatchManager进行触发事件;
- 从WatcherManager中筛选出符合条件的Watcher触发下发,并把映射给摘除。- 只会触发一次,且不会把真正的数据下发下去而只下发通知状态、事件类型、节点路径,具体变更数据由Client自行来查询;
- Watcher事件传递到- Client触发后也会删除映射关系。
 
- 只会触发一次,且不会把真正的数据下发下去而只下发
因此存在丢失的可能:
- 在对于同一个key或者同一范围的监听,若修改速度迅速,会漏事件。
3.2 Etcd
Etcd由两个协程和两个WatcherGroup来管理Watcher事件。详细见:Etcd Watch机制
- 带版本号监听;
- 连续监听不是单次监听。
4. 线性一致性
zab&Raft 只是共识算法,并非线性一致性,实现线性一致性需要上层做出努力。如etcd的线性一致性读。
什么是线性一致性读?
对于同一对象,读写请求按顺序线性进行。(读到最新写的数据)
 
        