程序锅

  • 首页
  • 分类
  • 标签
  • 归档
  • 关于

  • 搜索
基础知识 Etcd LeetCode 计算机体系结构 Kubernetes Containerd Docker 容器 云原生 Serverless 项目开发维护 ELF 深入理解程序 Tmux Vim Linux Kernel Linux numpy matplotlib 机器学习 MQTT 网络基础 Thrift RPC OS 操作系统 Clang 研途 数据结构和算法 Java 编程语言 Golang Python 个人网站搭建 Nginx 计算机通用技术 Git

Etcd 中 Raft 实现的概述

发表于 2022-12-03 | 分类于 Etcd | 0 | 阅读次数 2655

实现概述

Leader 选举

针对 leader 选举,etcd 基本是按照 Raft 协议中的方式实现的,其中有几点需要关注下,

  1. etcd 默认的心跳间隔时间(heartbeat-interval)是 100ms, 默认 election timeout 时间是 1000ms。需要根据实际部署环境、业务场景适当调优,否则就很可能会频繁发生 Leader 选举切换,导致服务稳定性下降。

  2. 针对上述 “当 Node A 网络恢复之后,由于 Node B/C 发现 Node A 的 term 值更大,但是 Node A 并不是 leader,此时会进行重新选举。但是,此时 Node A 的状态是远远落后于 Node B/C 的,是无法获得 leader 的,此次的选举相当于是无效的” 的问题。

    etcd 3.4 引入了一个 PreVote 参数(默认 false),该参数会启用 PreCandidate 状态,通过 PreCandidate 状态来解决该问题。Follower 在转换成 Candidate 状态前,会先进入 PreCandidate 状态,此时不自增任期号,仅发起预投票。若获得集群多数节点认可,确定有概率成为 Leader 才能进入 Candidate 状态,发起选举流程。那么,假设 Node A 节点数据落后较多,预投票请求是无法获得多数节点认可的,因此它就不会进入 Candidate 状态,导致集群重新选举。

日志复制

Etcd 中日志复制的实现与 Raft 中的流程类似,略有不同的是 Etcd 增加了 WAL 日志模块。整体流程如下所示,

  1. Leader 收到客户端的请求后,KV 模块会向 Raft 模块提交一个 put hello 为 world 的 proposal。采用的消息类型是 MsgProp。

  2. Raft 模块收到 MsgProp 提案消息后,

    • 会为此提案生成一个日志条目,并先追加到 unstable 存储中(不是下面的 Raft 日志存储,这里相当于 Raft 模块内部的一个缓存)。

    • 随后会遍历集群 Follower 列表和进度信息,为每个 Follower 生成追加(MsgApp)类型的 RPC 消息,此消息中包含待复制给 Follower 的日志条目。

    • 同时会维护两个核心字段来追踪各个 Follower 的进度信息,一个字段是 NextIndex, 它表示 Leader 发送给 Follower 节点的下一个日志条目索引,另一个字段是 MatchIndex,它表示 Follower 节点已复制的最大日志条目的索引。比如下图中,Follower C 的 MatchIndex 为 5,Follower A 的为 4。

  3. 从 Raft 模块获取待持久化的日志条目、待发送给 Follower 节点的消息、已提交的日志目录、HardState (都包含在 Ready 结构中)等内容。

    • 将日志条目持久化到 WAL 日志模块中,也会将已知的最大已提交日志条目的索引(committed index)、当前任期号、当前任期中投票的候选者 ID(都在 HardState 结构中)都会被写入到 WAL 中。

    • 将日志条目追加到 Raft Stable 存储(内存存储)中。

    • 通过网络模块发送消息(MsgApp)给 Follower,也会包含当前已知的最大已提交日志条目的索引。

    • 将已提交的日志条目通过 Apply 模块,应用到存储状态机。

  4. 各个 Follower 收到追加日志条目(MsgApp)消息后,

    • 它会持久化日志条目到 WAL 日志模块中,并将消息追加到 Raft Stable 存储(内存存储)中。
    • 随后向 Leader 回复一个应答追加日志条目(MsgAppResp)的消息,告知 Leader 当前已复制的日志最大索引。
  5. Leader 收到应答追加日志条目(MsgAppResp)消息后,Raft 模块会将 Follower 回复的已复制日志最大索引更新到跟踪 Follower 进展的 MatchIndex 字段。如下图所以,Follower C 的 MatchIndex 为 6,Follower A 为 5。

  6. Raft 模块根据所有 follower 的 MatchIndex 信息,计算出一个位置,如果这个位置已经被一半以上节点持久化,那么这个位置之前的日志条目都可以被标记为已提交。如下图所示,6 号索引位置之前的日志条目都已被多数节点复制,那么最大已提交的日志条目索引可更新为 6。

  7. 由于 Leader 从 Raft 模块中取 Ready 结构相当于是一个循环操作,因此此时就会按照取出 Ready 结构后的操作执行。这里主要关注的是将已提交的日志条目通过 Apply 模块,应用到存储状态机,同时将已提交的日志条目索引发送给 Follower。

相关链接

  1. Raft Example:https://github.com/etcd-io/etcd/blob/main/contrib/raftexample/README.md

  2. Etcd Raft库的工程化实现:https://www.codedump.info/post/20210515-raft

  3. etcd Raft库解析:https://www.codedump.info/post/20180922-etcd-raft/

卷死我
dawnguo 微信支付

微信支付

dawnguo 支付宝

支付宝

  • 本文作者: dawnguo
  • 本文链接: /archives/266
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Kubernetes # Etcd
Raft 算法原理
Etcd WAL 日志&&Snapshot
  • 文章目录
  • 站点概览
dawnguo

dawnguo

215 日志
24 分类
37 标签
RSS
Creative Commons
© 2018 — 2025 程序锅
0%