已复制命令
每一次锁状态变更都会作为下列命令之一被复制,作为日志条目装载进 AppendEntries 中,并在多数派提交之后由每个 节点按相同顺序应用。
命令(由 leader 提议)
| 命令 | 字段 | 含义 |
|---|---|---|
Grant | key、lease_ms | 获取一个 key。命令中不包含 token —— 每个节点的状态机会在应用时确定性地分配 token,因此每个节点都会计算出相同的 token |
Release | key | 释放一个 key |
Expire | key、token | 由 leader 触发的 lease 过期。只有 token 匹配时才会被移除 —— 这样一次迟到或重复的过期命令,就不会删除掉与此同时刚刚重新发放的锁(幂等) |
应用结果(状态机 → leader)
| 结果 | 含义 |
|---|---|
Granted { token } | 获取成功 —— 签发的围栏令牌 |
GrantRejected | Grant 到达时该 key 已被持有(防御性设计 —— 正常流程中不会发生) |
Released | 已释放 |
NotFound | 要释放的 key 不存在 |
Expired { existed } | 过期处理已完成 —— existed 表示是否真的移除了 |
Noop | 未发生任何操作 |
每个节点都用自己的本地时钟追踪 lease 的剩余时间,但实际的移除操作始终只通过 已提交的 Expire 命令进行——这也是为什么节点间的时钟漂移永远不会导致锁状态 出现分歧。如果客户端在收到响应之前断开连接,leader 会在授予之后立即为其 提议一次 Release,以便马上清理这个幽灵锁。
流程 —— 复制一次 Expire
领导者
跟随者 1
跟随者 2
通过本地时钟检测到 lease 过期
AppendEntries · Expire (key, token)
AppendEntries · Expire (key, token)
OK
多数派提交 → 每个节点按相同顺序应用
token 不匹配 → 被忽略 —— 保护刚刚发放的锁
共识 RPC(Raft)
即使是过期操作,也不过是又一条经过 leader 提议 → 多数派提交流程的命令。 得益于 token 校验,即使在提交被延迟期间同一个 key 被重新授予,一条延迟应用 的 Expire 也无法删除这把新锁。
术语
- 状态机 (state machine) —— 按顺序应用已提交命令、从而产生当前状态 (在这里就是锁表)的部分。由于每个节点都按相同顺序应用相同的命令, 它们总会收敛到相同的状态。
- 提交 (commit) —— 一个状态在被多数派节点记录之后所达到的"最终"状态。 只有已提交的命令才会被应用到状态机。
- 幂等 (idempotent) —— 一种特性:即使误将同一条命令应用两次,结果也不会改变。