李开龙 +

python编包工具setuptools

本文以 AsyncMapTest 类中的testAsyncMapPutGet函数为引导,来完完整整的梳理一遍state-log replication的流程,来归纳整个copycat代码的结构。

setuptools

Structuring Your Project

setup.cfg

pbr

setup.cfg example

entry_points in setup.cfg

entry_points demo

研究setuptools中为何能够将setup.cfg的entrypoints中console_scripts的函数转化为命令, 他跟files中的scripts有何区别? 如果有多个python包使用了同一个命令,怎么解决冲突?。

zsh oh my zsh Why Zsh is Cooler than Your Shell

replicate-state-machine-architecture

引用raft论文中一段话来描述日志复制的大体流程:

一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。领导人把这条指令作为一条新的日志条目附加到日志中去,然后并行的发起附加条目 RPCs 给其他的服务器,让他们复制这条日志条目。当这条日志条目被安全的复制,领导人会应用这条日志条目到它的状态机中然后把执行的结果返回给客户端。

日志复制主要讨论的是无论在哪种情况下, Leader 如何安全的将日志复制给集群中的其他节点。本文主要将 copycat 和 raft 论文的日志复制部分进行一一对照进行学习,目的有两点:

关于日志复制,论文中讨论的情况有:

因为要完完全全保证上述两种情况,论文中还提出了一些 safety (限制),所以在讨论上述两种情况前,先对 safety 进行介绍,然后再分别讨论这两种情况。(另外前提还有论文中出现的一些术语,例如 term, log index 等),引用raft论文中的图来说明log组成:

log-composition

Safety

为了保证整个过程的正确性,Raft算法保证以下属性时刻为真(这部分内容引用Raft一致性算法博客):

直观解释:

为了便于大家理解Raft算法的正确性,这里对于上述性质进行一些非严格证明。

Leader Crash

首先 Leader Crash 可能导致 Leader 和 Followers 的不一致, Followers 有如下几种情况:

在 Raft 算法中,Leader 处理不一致是通过强制跟随者直接复制自己的日志来解决了。这意味着在 Followers 中的冲突的日志条目会被领导人的日志覆盖。这里应用 Leader Append-Only 特性。

思路:领导人必须找到最后两者达成一致的地方,然后删除从那个点之后的所有日志条目,发送自己的日志给跟随者。由 Log Matching特性可知,在那个点之前两者的日志完全一致,之后由 Leader 安全的将日志附加给他,这样达到两者完全一致。

具体做法:领导人针对每一个跟随者维护了一个 nextIndex,这表示下一个需要发送给跟随者的日志条目的索引地址。当一个领导人刚获得权力的时候,他初始化所有的 nextIndex 值为自己日志中的最后一条。如果一个跟随者的日志和领导人不一致,那么在下一次的附加日志 RPC 时的一致性检查就会失败。在被跟随者拒绝之后,领导人就会减小 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得领导人和跟随者的日志达成一致。当这种情况发生,附加日志 RPC 就会成功,这时就会把跟随者冲突的日志条目全部删除并且加上领导人的日志。一旦附加日志 RPC 成功,那么跟随者的日志就会和领导人保持一致,并且在接下来的任期里一直继续保持。

代码分析:接着 leader election 来讨论,转换到 Leader 状态之后,立马做的事情:( LeaderState 中的代码)

@Override
public synchronized CompletableFuture<Void> open() {
return super.open()
  .thenRun(this::applyEntries)
  .thenRun(replicator::commit)
  .thenRun(this::takeLeadership)
  .thenRun(this::startHeartbeatTimer);
}

其中 replicator::commit为附加日志的入口,真正发送的函数是commit,该函数完成两个功能:发送附加日志请求AppendRequest给集群中Follower节点和处理来自集群Follower节点的附加日志AppendResponse。

下图是Leader向每个Followers发送的附加日志请求,图中每个字段都对应它的含义。 append-request 下图是附加日志的交互图 Follower-process-append-request

Follower or Candidate Crash

跟随者和候选人崩溃后的处理方式比领导人要简单的多,并且他们的处理方式是相同的。如果跟随者或者候选人崩溃了,那么后续发送给他们的 RPCs 都会失败。Raft 中处理这种失败就是简单的通过无限的重试;如果崩溃的机器重启了,那么这些 RPC 就会完整的成功。如果一个服务器在完成了一个 RPC,但是还没有响应的时候崩溃了,那么在他重新启动之后就会再次收到同样的请求。Raft 的 RPCs 都是幂等的,所以这样重试不会造成任何问题。例如一个跟随者如果收到附加日志请求但是他已经包含了这一日志,那么他就会直接忽略这个新的请求。

Blog

Opinion

Project