예제 #1
0
        private int ProcessRequestVote(Protocol p)
        {
            lock (this)
            {
                var r = p as RequestVote;
                if (LogSequence.TrySetTerm(r.Argument.Term))
                {
                    // new term found.
                    ConvertStateTo(RaftState.Follower);
                }
                // else continue process

                r.Result.Term = LogSequence.Term;
                // RequestVote RPC
                // Receiver implementation:
                // 1.Reply false if term < currentTerm(§5.1)
                // 2.If votedFor is null or candidateId, and candidate’s log is at
                // least as up - to - date as receiver’s log, grant vote(§5.2, §5.4)
                r.Result.VoteGranted = (r.Argument.Term >= LogSequence.Term) &&
                                       LogSequence.CanVoteFor(r.Argument.CandidateId) &&
                                       IsLastLogUpToDate(r.Argument.LastLogTerm, r.Argument.LastLogIndex);
                if (r.Result.VoteGranted)
                {
                    LogSequence.SetVoteFor(r.Argument.CandidateId);
                }
                logger.Debug("{0}: VoteFor={1} Rpc={2}", Name, LogSequence.VoteFor, r);
                r.SendResultCode(0);

                return(Procedure.Success);
            }
        }
예제 #2
0
        private void SendRequestVote(SchedulerTask ThisTask)
        {
            lock (this)
            {
                VoteSuccess.Clear(); // 每次选举开始清除。

                LeaderId = string.Empty;
                LogSequence.SetVoteFor(Name); // Vote Self First.
                LogSequence.TrySetTerm(LogSequence.Term + 1);
                WaitMajorityVoteTimoutTask?.Cancel();
                WaitMajorityVoteTimoutTask = null;

                var arg = new RequestVoteArgument();
                arg.Term        = LogSequence.Term;
                arg.CandidateId = Name;
                var log = LogSequence.LastRaftLog();
                arg.LastLogIndex = log.Index;
                arg.LastLogTerm  = log.Term;

                Server.Config.ForEachConnector(
                    (c) =>
                {
                    if (false == c.IsHandshakeDone)
                    {
                        return;
                    }
                    var rpc = new RequestVote()
                    {
                        Argument = arg
                    };
                    rpc.Send(c.Socket, (p) => ProcessRequestVoteResult(rpc, c));
                    logger.Debug("{0}: SendRequestVote {1}", Name, rpc);
                });

                // 定时,如果超时选举还未完成,再次发起选举。
                WaitMajorityVoteTimoutTask = Scheduler.Instance.Schedule(
                    (ThisTask) =>
                {
                    lock (this)
                    {
                        StartRequestVoteDelayTask = null;
                        ConvertStateTo(RaftState.Candidate);
                    }
                },
                    RaftConfig.AppendEntriesTimeout + 1000);
            }
        }
예제 #3
0
        private void ConvertStateFromFollwerTo(RaftState newState)
        {
            switch (newState)
            {
            case RaftState.Follower:
                logger.Info($"RaftState {Name}: Follower->Follower");
                return;

            case RaftState.Candidate:
                logger.Info($"RaftState {Name}: Follower->Candidate");
                State = RaftState.Candidate;
                LeaderLostTimerTask?.Cancel();
                LeaderLostTimerTask = null;
                LogSequence.SetVoteFor(string.Empty);     // 先清除,在真正自荐前可以给别人投票。
                StartRequestVote();
                return;

            case RaftState.Leader:
                // 并发的RequestVote的结果如果没有判断当前状态,可能会到达这里。
                // 不是什么大问题。see ProcessRequestVoteResult
                logger.Info($"RaftState {Name} Impossible! Follower->Leader");
                return;
            }
        }
예제 #4
0
        private void ConvertStateFromCandidateTo(RaftState newState)
        {
            switch (newState)
            {
            case RaftState.Follower:
                logger.Info($"RaftState {Name}: Candidate->Follower");
                State = RaftState.Follower;
                VoteSuccess.Clear();     // 选举结束清除。

                LogSequence.SetVoteFor(string.Empty);
                StartRequestVoteDelayTask?.Cancel();
                StartRequestVoteDelayTask = null;
                WaitMajorityVoteTimoutTask?.Cancel();
                WaitMajorityVoteTimoutTask = null;
                StartLeaderLostTimerTask();
                return;

            case RaftState.Candidate:
                logger.Info($"RaftState {Name}: Candidate->Candidate");
                LogSequence.SetVoteFor(string.Empty);     // 先清除,在真正自荐前可以给别人投票。
                StartRequestVote();
                return;

            case RaftState.Leader:
                StartRequestVoteDelayTask?.Cancel();
                StartRequestVoteDelayTask = null;
                WaitMajorityVoteTimoutTask?.Cancel();
                WaitMajorityVoteTimoutTask = null;
                VoteSuccess.Clear();     // 选举结束清除。

                logger.Info($"RaftState {Name}: Candidate->Leader");
                State = RaftState.Leader;
                LogSequence.SetVoteFor(string.Empty);
                LeaderId = Name;     // set to self

                // (Reinitialized after election)
                var nextIndex = LogSequence.LastIndex + 1;
                Server.Config.ForEachConnector(
                    (c) =>
                {
                    var cex        = c as Server.ConnectorEx;
                    cex.NextIndex  = nextIndex;
                    cex.MatchIndex = 0;
                });

                // Upon election:
                // send initial empty AppendEntries RPCs
                // (heartbeat)to each server; repeat during
                // idle periods to prevent election timeouts(§5.2)
                LogSequence.AppendLog(new HeartbeatLog(HeartbeatLog.SetLeaderReadyEvent), false);
                HearbeatTimerTask = Scheduler.Instance.Schedule(
                    (ThisTask) =>
                {
                    var elapse = Util.Time.NowUnixMillis - LogSequence.AppendLogActiveTime;
                    if (elapse < RaftConfig.LeaderHeartbeatTimer)
                    {
                        LogSequence.AppendLog(new HeartbeatLog(), false);
                    }
                },
                    1000,
                    1000);
                return;
            }
        }