Skip to content

Commit

Permalink
added Leader Election
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamhead committed Feb 6, 2022
1 parent d6af4b1 commit 35d872a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@
| fanout | 扇出 |
| incoming | 传入 |
| CommitIndex | 提交索引 |
| candidate | 候选者 |

2 changes: 1 addition & 1 deletion content/leader-and-followers.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ https://martinfowler.com/articles/patterns-of-distributed-systems/leader-followe
![领导者心跳](../image/leader-heartbeat.png)
<center>图3:领导者心跳</center>

对于三五个节点的小集群,比如在实现共识的系统中,领导者选举可以在数据集群内部实现,不依赖于任何外部系统。领袖选举发生在服务器启动时。每台服务器在启动时都会启动领导者选举,尝试选出一个领导者。在选出一个领导者之前,系统不会接收客户端的任何请求。正如在[世代时钟(Generation Clock)](generation-clock.md)模式中所阐释的那样,每次领导者选举都需要更新世代号。服务器总是处于三种状态之一:领导者、追随者或是寻找领导者(或候选人)。
对于三五个节点的小集群,比如在实现共识的系统中,领导者选举可以在数据集群内部实现,不依赖于任何外部系统。领袖选举发生在服务器启动时。每台服务器在启动时都会启动领导者选举,尝试选出一个领导者。在选出一个领导者之前,系统不会接收客户端的任何请求。正如在[世代时钟(Generation Clock)](generation-clock.md)模式中所阐释的那样,每次领导者选举都需要更新世代号。服务器总是处于三种状态之一:领导者、追随者或是寻找领导者(或候选者)。

```java
public enum ServerRole {
Expand Down
57 changes: 56 additions & 1 deletion content/replicated-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,59 @@ class ReplicatedLog…
applyLogEntries(previousHighWaterMark, request.getHighWaterMark());
}
}
```
```

#### 领导者选举

领导者选举就是检测到日志条目在前一个 Quorum 中完成提交的阶段。每个集群节点都会在三种状态下运行:候选者(candidate)、领导者(leader)和追随者(follower)。在追随者状态下,在启动时,集群节点会期待收到来自既有领导者的[心跳(HeartBeat)](heartbeat.md)。如果一个追随者在预先确定的时间段内没有听到领导者任何声音,它就会进入到候选者状态,开启领导者选举。领导者选举算法会建立一个新的[世代时钟(Generation Clock)](generation-clock.md)值。Raft 将[世代时钟(Generation Clock)](generation-clock.md)称为 term。

领导者选举机制也确保当选的领导者拥有 Quorum 所规定的最新日志条目。这是Raft所做的一个优化,避免了日志条目要从以前的 Quorum 转移新的领导者上。

新领导者选举的启动要通过向每个对等服务器发送消息,请求开始投票。

```java
class ReplicatedLog

private void startLeaderElection() {
replicationState.setGeneration(replicationState.getGeneration() + 1);
registerSelfVote();
requestVoteFrom(followers);
}
```

一旦服务器在某一[世代时钟(Generation Clock)](generation-clock.md)投票中得到投票,服务器总会为同样的世代返回同样的投票。这就确保了在选举成功发生的情况下,如果其它服务器以同样的世代请求投票,它是不会当选的。投票请求的处理过程如下:

```java
class ReplicatedLog

VoteResponse handleVoteRequest(VoteRequest voteRequest) {
//for higher generation request become follower.
// But we do not know who the leader is yet.
if (voteRequest.getGeneration() > replicationState.getGeneration()) {
becomeFollower(LEADER_NOT_KNOWN, voteRequest.getGeneration());
}

VoteTracker voteTracker = replicationState.getVoteTracker();
if (voteRequest.getGeneration() == replicationState.getGeneration() && !replicationState.hasLeader()) {
if(isUptoDate(voteRequest) && !voteTracker.alreadyVoted()) {
voteTracker.registerVote(voteRequest.getServerId());
return grantVote();
}
if (voteTracker.alreadyVoted()) {
return voteTracker.votedFor == voteRequest.getServerId() ?
grantVote():rejectVote();

}
}
return rejectVote();
}

private boolean isUptoDate(VoteRequest voteRequest) {
boolean result = voteRequest.getLastLogEntryGeneration() > wal.getLastLogEntryGeneration()
|| (voteRequest.getLastLogEntryGeneration() == wal.getLastLogEntryGeneration() &&
voteRequest.getLastLogEntryIndex() >= wal.getLastLogIndex());
return result;
}
```

收到大多数服务器投票的服务器会切换到领导者状态。这里的大多数是按照 [Quorum](quorum.md) 讨论的方式确定的。一旦当选,领导者会持续地向所有的追随者发送[心跳(HeartBeat)](heartbeat.md)。如果追随者在指定的时间间隔内没有收到[心跳(HeartBeat)](heartbeat.md),就会触发新的领导者选举。

0 comments on commit 35d872a

Please sign in to comment.