概述

etcd 诞生于CoreOS公司,当前隶属于CNCF基金会,供高可用、强一致的小型key value数据存储服务。

架构

  1. 主要的模块有:
  • gRPC server:负责对外提供gRPC接口,目前最新稳定版本已经支持http访问接口。用于处理用户发送的 API 请求以及其它 etcd 节点的同步与心跳信息请求
  • raft 状态机:Raft 强一致性算法的具体实现,是 etcd 的核心
  • wal 日志存储:Write Ahead Log(预写式日志),是 etcd 的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd 就通过 WAL 进行持久化存储。WAL 中,所有的数据提交前都会事先记录日志。
  • Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。
  • boltdb:kv数据的存储引擎,v3支持不同的后端存储,当前采用boltdb,通过boltdb支持事务操作。
  1. 主要有四种角色:
  • leader:负责日志的同步管理,处理来自客户端的请求,与Follower保持心跳的联系
  • follower:刚启动时所有节点为follower状态,响应leader的日志同步请求,响应candidate的请求,把请求到follower的事务转发给leader
  • candidate:负责选举投票,Raft刚启动时由一个节点从Follower转为Candidate发起选举,选举出; leader后从candidate转为leader状态;当follower收不到leadr的心跳之后,也会变成candidate状态
  • client:请求发起者

概念

  1. proxy etcd的一种模式,为 etcd 集群提供反向代理服务
  2. term 某个节点成为leader到下一次竞选时间,称为一个term
  3. revision revision 代表的是全局数据的版本。当数据发生变更,包括创建、修改、删除,其 revision 对应的都会+1。特别的,在集群中跨 leader 任期之间,revision 都会保持全局单调递增。正是 revision 的这一特性,使得集群中任意一次的修改都对应着一个唯一的 revision,因此我们可以通过 revision 来支持数据的MVCC,也可以支持数据的watch。对于每一个 key value 数据节点,etcd 中都记录了三个版本:
    • 第一个版本叫做 create_revision,是 key value 在创建时对应的 revision;
    • 第二个叫做 mod_revision,是其数据被操作的时候对应的 revision;
    • 第三个 version 就是一个计数器,代表了 key value 被修改了多少次。
  4. watch 使用watch订阅数据时,可以支持从任意历史时刻(指定 revision)开始创建一个 watcher,在客户端与 etcd 之间建立一个数据管道,etcd 会推送从指定 revision 开始的所有数据变更。etcd 提供的 watch 机制保证,该 Key 的数据后续的被修改之后,通过这个数据管道即时的推送给客户端。
  5. lease租约 lease service提供租约的支持。lease 是一种检测客户端存活状况的机制。集群授予具有生存时间的租约。如果 etcd 集群在给定的 TTL 时间内未收到keepAlive,则租约到期。 为了将租约绑定到键值存储中,每个 key 最多可以附加一个租约。当租约到期或被撤销时,该租约所附的所有 key 都将被删除。每个过期的密钥都会在事件历史记录中生成一个删除事件。

核心raft协议

Raft协议采用分治的思想,把分布式协同的问题分为3个问题:

  • 选举: 一个新的集群启动时,或者老的leader故障时,会选举出一个新的leader。
  • 日志同步: leader必须接受客户端的日志条目并且将他们同步到集群的所有机器。
  • 数据安全: 保证任何节点只要在它的状态机中生效了一条日志条目,就不会在相同的key上生效另一条日志条目。
leader选举
  • 初始状态下,大家都是平等的follower,每个follower内部都维护了一个随机的timer。在timer时间到了的时候还没有人主动联系它的话,那它就要变成candidate,同时发出投票请求(RequestVote)给其他人。RequestVote一般包含如下信息:
    • term,自身处于的选举周期
    • lastLogIndex,log中最新的index值
    • lastLogTerm,log中最近的index是在哪个term中产生的
  • 对于相同条件的candidate,follower们采取先来先投票的策略。如果超过半数的follower都认为他是合适做领导的,那么恭喜,新的leader产生了。如果没有人愿意选这个悲剧的candidate,那它只有老老实实的变回小弟的状态。而follower投票的时候也会和自己对比下RequestVote相关信息,以确保candidate上存储的数据是最新的:
    • 如果term < currentTerm,也就是说candidate的版本还没我新,返回 false
    • 如果已经投票给别的candidate了(votedFor),则返回false
    • log匹配,如果和自身的log匹配上了,则返回true
  • 选举完成之后,leader会定时发送心跳检测(heart beat)给follower,follower通过心跳来感知leader的存在的。
  • 如果follower在timer期间内没有收到leader的心跳,这时很可能leader已经跪了,新的一轮(term)选举就开始了。
数据同步

同步的流程如下:

  • 集群某个节点收到 client 的 put 请求要求修改数据。节点会生成一个 Type 为 MsgProp 的 Message,发送给 leader。
  • leader 收到 Message 以后,会处理 Message 中的日志条目,将其 append 到 raftLog 的 unstable 的日志中,并且调用 bcastAppend()广播 append 日志的消息。
    • leader 中有协程处理 unstable 日志和刚刚准备发送的消息,newReady 方法会把这些都封装到 Ready 结构中。
    • leader 的另一个协程处理这个 Ready,先发送消息,然后调用 WAL 将日志持久化到本地磁盘。WAL 中保存的持久化的日志条目会有一个定时任务定时删除。
  • follower 收到 append 日志的消息,会调用它自己的 raftLog,将消息中的日志 append 到本地缓存中。随后 follower 也像 leader 一样,有协程将缓存中的日志条目通过WAL持久化到磁盘中并将当前已经持久化的最新日志 index 返回给 leader。
  • leader收到follower的回复之后,如果超过半数的节点都写日志成功,那么leader会把更新数据commit到boltdb存储,然后给所有follower发起第二次持久化数据到boltdb的广播,超过半数的follower commit成功则此次写成功。
数据安全

raft为了保证数据的强一致性,所有的数据流向都是一个方向,从 leader 流向 follower,也就是所有 follower 的数据必须与 leader 保持一致,如果不一致会被覆盖。即所有用户更新数据的请求都最先由 leader 获得,然后存下来通知其他节点也存下来,等到大多数节点反馈时再把数据提交。一个已提交的数据项才是 raft 真正稳定存储下来的数据项,不再被修改,最后再把提交的数据同步给其他 follower。因为每个节点都有 raft 已提交数据准确的备份(最坏的情况也只是已提交数据还未完全同步),所以读的请求任意一个节点都可以处理。

应用场景

和zookeeper一样

优缺点

对比zookeeper的优点

  • 简单:易于部署,易使用。基于 HTTP+JSON 的 API 让你用 curl 就可以轻松使用
  • 安全:可选 SSL 客户认证机制
  • 快速:每个实例每秒支持一千次写操作
  • 可信:使用一致性 Raft 算法充分实现了分布式

缺点

  • 在网络分区时,当 leader 处于小分区时,读请求会继续被处理

总结

etcd目前背靠CNCF,有k8s背书,作为新一代的协调服务未来可期,应该会逐步取代zookeeper。