ETCD-內部原理

 

不管是Paxos仍是Raft,它們都是致力於維護一RSM(Replicated State Machine),如上圖所示。對於RSM來講,狀態存儲是很是關鍵的算法

 

(Replicated State Machine)狀態機:一致性group的節點的某個時刻的狀態(好比數據庫裏x=1,y=1是一個狀態)轉移能夠當作自動機裏的一個狀態,因此叫狀態機。 
Replicated Log: 包含了來自客戶端的用於執行在狀態機上的命令(操做)。數據庫

 

 

ETCD架構圖centos

 

Snapshot處理流程:https://www.jianshu.com/p/cf4f0d3ae253安全

從etcd的架構圖中能夠看到,etcd主要分爲四個部分:網絡

 HTTP Server: 用於處理用戶發送的API請求以及其它etcd節點的同步與心跳信息請求。架構

Store:這個模塊顧名思義,就像一個商店把etcd已經準備好的各項底層支持加工起來,爲用戶提供五花八門的API支持,處理用戶的各項請求。併發

用於處理etcd支持的各種功能的事務,包括數據索引、節點狀態變動、監控與反饋、事件處理與執行等,是etcd對用戶提供的大多數API功能的具體實現。分佈式

Raft: raft 狀態機,raft強一致性算法的具體實現,是etcd的核心。性能

 WAL:Write Ahead Log(預寫式日誌),是etcd的數據存儲方式。除了在內存中存有全部數據的狀態以及節點的索引之外,etcd就經過WAL進行持久化存儲。WAL中,全部的數據提交前都會事先記錄日誌。Entry表示存儲的具體日誌內容。 優化

隨着使用量的增長,當WAL文件中數據項內容過大達到設定值(默認爲10000)時,會進行WAL的切分,同時進行snapshot操做,通過snapshot之後的WAL文件就能夠刪除。而經過API能夠查詢的歷史etcd操做默認爲1000條。實際上數據目錄中有用的snapshot和WAL文件各只有一個,默認狀況下etcd會各保留5個歷史文件。

故障快速恢復/回滾(undo)/重作(redo) :全部的修改操做都被記錄在WAL,能夠經過執行全部WAL中記錄的修改操做,快速從最原始的數據恢復到以前的狀態。 

一般,一個用戶的請求發送過來,會經由HTTP Server轉發給Store進行具體的事務處理,若是涉及到節點的修改,則交給Raft模塊進行狀態的變動、日誌的記錄,而後再同步給別的etcd節點以確認數據提交,最後進行數據的提交,再次同步。

 

集羣選主

Raft協議是用於維護一組服務節點數據一致性的協議。這一組服務節點構成一個集羣,而且有一個主節點來對外提供服務。當集羣初始化,或者主節點掛掉後,面臨一個選主問題。集羣中每一個節點,任意時刻處於Leader(主), Follower(從), Candidate(候選)這三個角色之一。

 

選舉特色以下:

•當集羣初始化時候,每一個節點都是Follower角色;

•集羣中存在至多1個有效的主節點,經過心跳與其餘節點同步數據;

•當Follower在必定時間內沒有收到來自主節點的心跳,會將本身角色改變爲Candidate,併發起一次選主投票;當收到包括本身在內超過半數節點同意後,選舉成功;當收到票數不足半數選舉失敗,或者選舉超時。若本輪未選出主節點,將進行下一輪選舉(出現這種狀況,是因爲多個節點同時選舉,全部節點均爲得到過半選票)。

•Candidate節點收到來自主節點的信息後,會當即終止選舉過程,進入Follower角色。

爲了不陷入選主失敗循環,每一個節點未收到心跳發起選舉的時間是必定範圍內的隨機值,這樣可以避免2個節點同時發起選主。

 

集羣大小與容錯

 

集羣的大小指集羣節點的個數。根據 etcd 的分佈式數據冗餘策略,集羣節點越多,容錯能力(Failure Tolerance)越強,同時寫性能也會越差。因此關於集羣大小的優化,其實就是容錯和寫性能的一個平衡。

 ETCD推薦使用【奇數】做爲集羣節點個數。緣由有二:

1、奇數個節點與和其配對的偶數個節點相比(好比 3節點和4節點對比),容錯能力相同,卻能夠少一個節點。

2、偶數個節點集羣不可用風險更高,表如今選主過程當中,有較大機率等額選票,從而出發下一輪選舉。

因此綜合考慮性能和容錯能力,etcd 官方文檔推薦的 etcd 集羣大小是 3, 5, 7。至於到底選擇 3,5 仍是 7,根據須要的容錯能力而定。

關於節點數和容錯能力對應關係,以下表所示:

 

 

 

日誌複製

所謂日誌複製,是指主節點將每次操做造成日誌條目,並持久化到本地磁盤,而後經過網絡IO發送給其餘節點。其餘節點根據日誌的邏輯時鐘(TERM)和日誌編號(INDEX)來判斷是否將該日誌記錄持久化到本地。當主節點收到包括本身在內超過半數節點成功返回,那麼認爲該日誌是可提交的(committed),並將日誌輸入到狀態機,將結果返回給客戶端。

這裏須要注意的是,每次選主都會造成一個惟一的TERM編號,至關於邏輯時鐘。每一條日誌都有全局惟一的編號。

 

主節點經過網絡IO向其餘節點追加日誌。若某節點收到日誌追加的消息,首先判斷該日誌的TERM是否過時,以及該日誌條目的INDEX是否比當前以及提交的日誌的INDEX跟早。若已過時,或者比提交的日誌更早,那麼就拒絕追加,並返回該節點當前的已提交的日誌的編號。不然,將日誌追加,並返回成功。

當主節點收到其餘節點關於日誌追加的回覆後,若發現有拒絕,則根據該節點返回的已提交日誌編號,發送其編號下一條日誌。

主節點像其餘節點同步日誌,還做了擁塞控制。具體地說,主節點發現日誌複製的目標節點拒絕了某第二天志追加消息,將進入日誌探測階段,一條一條發送日誌,直到目標節點接受日誌,而後進入快速複製階段,可進行批量日誌追加。

按照日誌複製的邏輯,咱們能夠看到,集羣中慢節點不影響整個集羣的性能。另一個特色是,數據只從主節點複製到Follower節點。 

 

安全性 

 

截止此刻,選主以及日誌複製並不能保證節點間數據一致。試想,當一個某個節點掛掉了,一段時間後再次重啓,並當選爲主節點。而在其掛掉這段時間內,集羣如有超過半數節點存活,集羣會正常工做,那麼會有日誌提交。這些提交的日誌沒法傳遞給掛掉的節點。當掛掉的節點再次當選主節點,它將缺失部分已提交的日誌。在這樣場景下,按Raft協議,它將本身日誌複製給其餘節點,會將集羣已經提交的日誌給覆蓋掉。

這顯然是不可接受的。

 

其餘協議解決這個問題的辦法是,新當選的主節點會詢問其餘節點,和本身數據對比,肯定出集羣已提交數據,而後將缺失的數據同步過來。這個方案有明顯缺陷,增長了集羣恢復服務的時間(集羣在選舉階段不可服務),而且增長了協議的複雜度。

Raft解決的辦法是,在選主邏輯中,對可以成爲主的節點加以限制,確保選出的節點已定包含了集羣已經提交的全部日誌。若是新選出的主節點已經包含了集羣全部提交的日誌,那就不須要從和其餘節點比對數據了。簡化了流程,縮短了集羣恢復服務的時間。

 

這裏存在一個問題,加以這樣限制以後,還可否選出主呢?答案是:只要仍然有超過半數節點存活,這樣的主必定可以選出。由於已經提交的日誌必然被集羣中超過半數節點持久化,顯然前一個主節點提交的最後一條日誌也被集羣中大部分節點持久化。當主節點掛掉後,集羣中仍有大部分節點存活,那這存活的節點中必定存在一個節點包含了已經提交的日誌了。 

 

 

 

相關文章
相關標籤/搜索