概述
etcd 誕生於CoreOS公司,當前隸屬於CNCF基金會,供高可用、強一致的小型key value數據存儲服務。算法
架構
- 主要的模塊有:
- gRPC server:負責對外提供gRPC接口,目前最新穩定版本已經支持http訪問接口。用於處理用戶發送的 API 請求以及其它 etcd 節點的同步與心跳信息請求
- raft 狀態機:Raft 強一致性算法的具體實現,是 etcd 的核心
- wal 日誌存儲:Write Ahead Log(預寫式日誌),是 etcd 的數據存儲方式。除了在內存中存有全部數據的狀態以及節點的索引之外,etcd 就經過 WAL 進行持久化存儲。WAL 中,全部的數據提交前都會事先記錄日誌。
- Snapshot 是爲了防止數據過多而進行的狀態快照;Entry 表示存儲的具體日誌內容。
- boltdb:kv數據的存儲引擎,v3支持不一樣的後端存儲,當前採用boltdb,經過boltdb支持事務操做。
- 主要有四種角色:
- leader:負責日誌的同步管理,處理來自客戶端的請求,與Follower保持心跳的聯繫
- follower:剛啓動時全部節點爲follower狀態,響應leader的日誌同步請求,響應candidate的請求,把請求到follower的事務轉發給leader
- candidate:負責選舉投票,Raft剛啓動時由一個節點從Follower轉爲Candidate發起選舉,選舉出;
leader後從candidate轉爲leader狀態;當follower收不到leadr的心跳以後,也會變成candidate狀態
- client:請求發起者
概念
- proxy
etcd的一種模式,爲 etcd 集羣提供反向代理服務
- term
某個節點成爲leader到下一次競選時間,稱爲一個term
- 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 被修改了多少次。
- watch
使用watch訂閱數據時,能夠支持從任意歷史時刻(指定 revision)開始建立一個 watcher,在客戶端與 etcd 之間創建一個數據管道,etcd 會推送從指定 revision 開始的全部數據變動。etcd 提供的 watch 機制保證,該 Key 的數據後續的被修改以後,經過這個數據管道即時的推送給客戶端。
- 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的優勢