欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分布式一致算法raft

程序员文章站 2022-07-12 17:12:57
...

【raft算法起源】

随着分布式系统的普及,系统内各存储节点的数据一致性问题成为分布式系统的关键点。raft算法是为了解决分布式系统中的数据一致性问题而产生的。除了raft外,还有其他的分布式一致性算法Paxos, ZAB等。

 

【raft术语】

名称 含义

初始值

属性

C(通用)/

L(leader专有)

是否持久化

Y(持久化)

N(非持久化)

leader 中心节点 NA NA NA
follower 跟随节点 NA NA NA
candidate 候选节点 NA NA NA
currentTerm 当前周期 0 C Y
votedFor 点赞同的节点ID null C

Y

log[] 日志条目数组;每个日志条目包含操作命令和生成该日志条目时leader的周期 null C Y
commitIndex 可以提交的日志索引 0 C N
lastApplied 最近应用的日志索引 0 C N
nextIndex[] 下一次给每个follower发送的日志索引 null L N
matchIndex[] 每个follower匹配的日志索引 null L N

 

 

 

【raft基本思想】

分布式系统中选举出一个中心节点leader,其他节点为follower;当客户端发送写请求时,交由leader处理(每个follower节点中记录了leader信息),leader生成logEntry记录并并行发送给每个follower,当集群过半节点完成logEntry记录后,leader执行提交操作并应用logEntry到存储层,执行成功后返回给客户端;当客户端发送读请求时,也由leader处理,保证读到最新的数据。

【几个关键点】

  • leader的选举

每个节点维护一个minumElectionTime,超过这个时间未收到leader的心跳请求,节点进入选举状态,向其他节点发送VoteRequestRPC请求,请求中包含的信息如下, 收到请求的节点根据本节点的信息判断是否投票给请求节点,具体步骤如下,当某个节点收到半数以上的投票时,该节点成为leader并通过AppendEntriesRPC与follower保持通信。

pulic class ResponseVoteRPC {
    boolean voteGranted; // accept or not
    int term; // currentTerm of receiver
    ... // others
}

public class RequestVoteRPC {
    int term;
    int candidateId;
    int lastLogIndex;
    int lastLogTerm;
    ... // others
}


// implementation of receiver
ResponseVoteRPC voteFor(RequestVoteRPC req) {
    ... // parse req and get parameters
    if (currentTerm > term) {
        return new ResponseVoteRPC(currentTerm); // reject vote
    }

    if (currentTerm < term) {
        currentTerm = term; // set currentTerm
        return new ResponseVoteRPC(); // grant vote
    }

    if (currentLastLogTerm > lastLogTerm) {
        return new ResponseVoteRPC(currentTerm); // reject vote
    }

    if (currentLastLogTerm < lastLogTerm) {
        return new ResponseVoteRPC(); // grant vote
    }

    if (currentLastLogIndex > lastLogIndex) {
        return new ResponseVoteRPC(currentTerm); // reject vote
    }

    return new ResponseVoteRPC(); // grant vote
}
  • 有序更新&日志同步

leader除了向各follower同步心跳包(AppendEntriesRPC请求,其中logEntries为空)外,还因更新操作向各follower同步日志(AppendEntriesRPC请求,其中logEntries非空,为待同步的操作日志),为了保证各follower中操作日志的顺序同leader中的日志顺序完全一致,AppendEntriesRPC请求中增加了日志直接前驱信息,即本次同步日志的前一条日志的索引和周期,当follower收到请求后,查找本地是否有日志直接前驱信息,即根据前驱索引找到前驱日志再比较周期是否一致,如果匹配成功,则本次同步成功,否则同步失败,leader将前驱日志索引递减后重试,直到找到匹配的索引。

  • 网络分区

当网络出现分区时,分区内的节点间可以通信,分区间的节点无法通信,如果这些分区都是可用分区,则会出现多个leader,最终导致数据不一致,因此需要规定一个可用分区(包含集群半数以上节点),比如5个节点A, B, C, D, E, 网络分区A, B, C为可用分区,网络分区D, E为不可用分区,此时集群还可以提供服务。

  • 成员变化

当集群中有节点加入或退出时,即集群的成员发生了变化,需要一个过渡期防止脑裂以及数据不一致的问题。成员信息的变化由leader通过AppendEntriesRPC请求的方式同步给各follower,同步的成员包括两部分:新集群成员和老集群成员,每个follower收到该请求后,使用新集群的成员信息,联合一致性由新老成员共同保证。

【参考资料】

  1. https://raft.github.io/
  2. https://raft.github.io/raft.pdf

 

相关标签: 存储 分布式