Raft协议问题总结(一): Leader 选举的异步选举与提前决策

一. 问题背景

在raft协议的运行中,可能会由于网络故障、机器故障等等原因使先前的Leader挂掉。而raft有一个timeout机制,即如果follower超过timeout时间没有收到来自Leader的广播,便会成为Candidate开始选举试图成为Leader。

1.1 Leader选举

Leader选举的流程如代码所示,首先执行Candidate逻辑,决定哪个raft节点成为Candidatem,并通过随机时间机制保证每个时间点有劲仅有一个节点成为Candidate;然后,Candidate发送投票请求给各个节点,各节点根据term和Index两个信息决定是否投票;最后,Candidate收集各节点投票回复结果,投票数大于一半则成为Leader,否则变为follower。值得注意的是,Candidate在发送投票的过程中,一旦发现比自己更高的Term会立马成为follower。

func (rf *Raft) electionLoop() {
    for !rf.killed() {
      time.Sleep(10*time.Millisecond)
          func() {
                // 1. 执行Candidate逻辑
               
                //2. 发送投票请求
           
                //3. 统计投票结果
                
                //4. 决策是否成为Leader
        }()
    }
}
 

二. 同步选举

2.1 同步选举

在同步选举下,如代码所示,我们会用一把锁 rf.mu.Lock() 锁住整个过程,这意味着一个raft节点走完Candiate、投票请求和统计投票结果这三个逻辑后,后续节点才能进行选举过程。如下图示例,raft1、raft2依次执行Leader 选举逻辑,上一个raft节点未结束选举,后面的raft节点不能开启选举过程。

func (rf *Raft) electionLoop() {
    for !rf.killed() {
      time.Sleep(10*time.Millisecond)
          func() {
                rf.mu.Lock()
                defer rf.mu.Unlock()
                // 1. 执行Candidate逻辑
               for peerId := 0; peerId < len(rf.peers); peerId++ {
                  go func(id int) {
                //2. 发送投票请求
                  }(peerId)
                }
                maxTerm := 0
                for {
                  select {
                  case voteResult := <-voteResultChan:
                //3. 统计投票结果
                //4. 决策是否成为Leader
        }()
    }
}
 

2.2 同步选举的问题

如上图所示,在同步选举的代码逻辑下: 即当且仅当上一节点选举结束后,当前节点才开始继续进行选举过程。在此情形下,当发生如下面test代码中disconnect某个raft节点时,Candidate发送给该节点的投票信息会由于没有收到回复而一直等待,那么此节点的选举过程会一直等在统计投票阶段,等待follower的回复,由此会导致选举超时失败。

  
  leader1 := cfg.checkOneLeader()
​
    // if the leader disconnects, a new one should be elected.
    cfg.disconnect(leader1)
    cfg.checkOneLeader()
  /*
  ......省略......
    */
    cfg.end()
}

三. 异步选举与提前决策

3.1 异步选举

为了解决2.2同步选举的问题,我们采用异步的方式进行Leader选举,其代码核心就是:rf.mu.Unlock(),即并发1. 发送投票请求,2. 收集投票结果 并发这两个过程。如图所示,当我们并发这两个过程时,由于各个节点并发执行Leader选举过程,raft节点因网络故障而未收到回复不会影响其他节点的选举过程。通过这样异步选举的方式即可解决同步选举的问题。

func (rf *Raft) electionLoop() {
    for !rf.killed() {
        time.Sleep(10*time.Millisecond)
​
        func() {
              rf.mu.Lock()
              defer rf.mu.Unlock()
        /*执行了一段逻辑
        time.Sleep(1*time.second)
        */
      //-------------------------解锁:异步选举------------------------
      //-------------------------未解锁:同步选举-----------------------
                rf.mu.Unlock()   //解锁
                voteCount := 1   // 收到投票个数(先给自己投1票)
                finishCount := 1 // 收到应答个数
                voteResultChan := make(chan *VoteResult, len(rf.peers))
      
                for peerId := 0; peerId < len(rf.peers); peerId++ {
            go func(id int) {
              if id == rf.me {
                return
              }
              resp := RequestVoteReply{}
              if ok := rf.sendRequestVote(id, &args, &resp); ok {
                voteResultChan <- &VoteResult{peerId: id, resp: &resp}
              } else {
                voteResultChan <- &VoteResult{peerId: id, resp: nil}
              }
            }(peerId)
                }
                maxTerm := 0
                for {
            select {
            case voteResult := <-voteResultChan:
                finishCount += 1
                if voteResult.resp != nil {
                    if voteResult.resp.VoteGranted {
                      voteCount += 1
                    }
                    if voteResult.resp.Term > maxTerm {
                      maxTerm = voteResult.resp.Term
                    }
                    /*此处为决策是否成为Leader的代码
                    1.------------------无提前决策-----------
                    2.------------------有提前决策-----------
                    */
        }()
    }
}
 

3.2 提前决策

有了异步选举,各节点选举过程的开启可以互不影响。但这还不够,因为网络故障而导致收不到回复的情况仍然存在,如果Candidate必须等到收到所有投票信息才决策是否成为Leader的话,那么Candidate仍然无法决策是否成为Leader。而Leader选举的规则中,我们仅仅只需收到大于一半的赞同票即可成为Leader。这就意味着Candidate只有收到大于一半的赞同票就可以提前决策是否成为Leader,同样收到大于一半的否定票也可提前结束选举过程成为follower。

//---------------------------提前决策---------------------------
     //-----当收到过半的投票时,转到该节点直接成为leader,并立马发送广播-----
                if finishCount == len(rf.peers) || voteCount > len(rf.peers)/2 {
                    goto VOTE_END
                }
            }
                }
      
            VOTE_END:   
          rf.mu.Lock()
          time.Sleep(1*time.Second) //执行了一段逻辑
          // 赢得大多数选票,则成为leader
          if voteCount > len(rf.peers)/2 {
            rf.role = ROLE_LEADER
            rf.leaderId = rf.me
            rf.lastBroadcastTime = time.Unix(0, 0) //令appendEntries广播立即执行
            return
          }
            }

四.总结

通过异步选举和提前决策,我们能完美的解决Leader选举过程中,由于网络故障而导致的选举失败问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值