Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://vearne.cc

1. 前言

我在 聊聊raft的一个实现(2) 中的文末曾提到

只要client等到leader完成Commit动作。即使后续leader发生变更或部分节点崩溃,raft协议可以保证,client所提交的改动依然有效。

本文将举个例子,来说明一下。

2. 示例

如下图,假定cluster中有S1、S2、S3、S4、S5共5个节点,每个方格表示一条日志,方格中的数字是日志对应的term, 当前的leader是S1
image_1ctkv3tmtpi2jghjbf1uspjbp9.png-25.8kB

commitIndex表示当前已经提交的日志,也就是成功同步到majority的日志位置(LogIndex)的最大值。
至少有3个node包含了LogIndex1、2、3,因此commitIndex的值是3
参看raft/server.go的processAppendEntriesResponse 函数了解commitIndex的计算方法

    // Determine the committed index that a majority has.
    var indices []uint64
    indices = append(indices, s.log.currentIndex())
    for _, peer := range s.peers {
        indices = append(indices, peer.getPrevLogIndex())
    }
    sort.Sort(sort.Reverse(uint64Slice(indices)))

    // We can commit up to the index which the majority of the members have appended.
    commitIndex := indices[s.QuorumSize()-1]
    committedIndex := s.log.commitIndex

    if commitIndex > committedIndex {
        // leader needs to do a fsync before committing log entries
        s.log.sync()
        s.log.setCommitIndex(commitIndex)
        s.debugln("commit index ", commitIndex)
    }

想象一个最糟糕的场景S1、S2,因为某种原因同时崩溃了。在goraft中,节点即使崩溃,也不会从peers中删除,也就是说cluster中节点的数目没有发生变化,要想保证剩下的节点能够正常选出leader,节点的数量不能少于3(集群节点总数/2 + 1)。

image_1ctkv3tmtpi2jghjbf1uspjbp9.png-25.8kB

这个时候,谁可能被选为leader, 注意:raft协议中这样的要求

candidate’s log is at least as up-to-date as receiver’s log then vote

解释起来,就是必须同时满足以下2个条件,才会给candidate投票

  • candidate.LastLogTerm >= receiver.LastLogTerm
  • candidate.LastLogIndex >= receiver.LastLogIndex

参看 raft/server.go processRequestVoteRequest 函数

    // If the candidate's log is not at least as up-to-date as our last log then don't vote.
    lastIndex, lastTerm := s.log.lastInfo()
    if lastIndex > req.LastLogIndex || lastTerm > req.LastLogTerm {
        s.debugln("server.deny.vote: cause out of date log: ", req.CandidateName,
            "Index :[", lastIndex, "]", " [", req.LastLogIndex, "]",
            "Term :[", lastTerm, "]", " [", req.LastLogTerm, "]")
        return newRequestVoteResponse(s.currentTerm, false), false
    }
node LastLogTerm LastLogIndex
S3 1 3
S4 1 2
S5 1 1

从上表可知,只有S3能够当选为leader, S3包含了LogIndex为3的日志,那么后续在S3的任期内,此条日志会被S3分发给S4和S5。
只要leader完成Commit动作,即使后续leader发生变更或部分节点崩溃,client所提交的改动依然有效,这个结论是正确的。
image_1ctl4lpa11ch81t3q1nus1ohn1gd69.png-229.8kB


如果我的文章对你有帮助,你可以给我打赏以促使我拿出更多的时间和精力来分享我的经验和思考总结。

微信支付码

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据