文章大部分引至:http://jolestar.com/etcd-architecture/java
Etcd 按照官方介紹node
Etcd is a distributed, consistent key-value store for shared configuration and service discoverygit
是一個分佈式的,一致的 key-value 存儲,主要用途是共享配置和服務發現。Etcd 已經在不少分佈式系統中獲得普遍的使用,本文的架構與實現部分主要解答如下問題:github
全部的分佈式系統,都面臨的一個問題是多個節點之間的數據共享問題,這個和團隊協做的道理是同樣的,成員能夠分頭幹活,但老是須要共享一些必須的信息,好比誰是 leader, 都有哪些成員,依賴任務之間的順序協調等。因此分佈式系統要麼本身實現一個可靠的共享存儲來同步信息(好比 Elasticsearch ),要麼依賴一個可靠的共享存儲服務,而 Etcd 就是這樣一個服務。golang
Etcd 主要提供如下能力,已經熟悉 Etcd 的讀者能夠略過本段。docker
更詳細的使用場景不在這裏描述,有興趣的能夠參看文末infoq的一篇文章。apache
說到這個就不得不提及raft協議。但這篇文章不是專門分析raft的,篇幅所限,不能詳細分析,有興趣的建議看raft協議的一個動畫。便於看後面的文章,我這裏簡單作個總結:json
Etcd 實現raft的時候,充分利用了go語言CSP併發模型和chan的魔法,想更進行一步瞭解的能夠去看源碼,這裏只簡單分析下它的wal日誌。後端
wal日誌是二進制的,解析出來後是以上數據結構LogEntry。其中第一個字段type,只有兩種,一種是0表示Normal,1表示ConfChange(ConfChange表示 Etcd 自己的配置變動同步,好比有新的節點加入等)。第二個字段是term,每一個term表明一個主節點的任期,每次主節點變動term就會變化。第三個字段是index,這個序號是嚴格有序遞增的,表明變動序號。第四個字段是二進制的data,將raft request對象的pb結構整個保存下。Etcd 源碼下有個tools/etcd-dump-logs,能夠將wal日誌dump成文本查看,能夠協助分析raft協議。網絡
raft協議自己不關心應用數據,也就是data中的部分,一致性都經過同步wal日誌來實現,每一個節點將從主節點收到的data apply到本地的存儲,raft只關心日誌的同步狀態,若是本地存儲實現的有bug,好比沒有正確的將data apply到本地,也可能會致使數據不一致。
Etcd v2 和 v3 本質上是共享同一套 raft 協議代碼的兩個獨立的應用,接口不同,存儲不同,數據互相隔離。也就是說若是從 Etcd v2 升級到 Etcd v3,原來v2 的數據仍是隻能用 v2 的接口訪問,v3 的接口建立的數據也只能訪問經過 v3 的接口訪問。因此咱們按照 v2 和 v3 分別分析。
Etcd v2 是個純內存的實現,並未實時將數據寫入到磁盤,持久化機制很簡單,就是將store整合序列化成json寫入文件。數據在內存中是一個簡單的樹結構。好比如下數據存儲到 Etcd 中的結構就如圖所示。
/nodes/1/name node1
/nodes/1/ip 192.168.1.1
store中有一個全局的currentIndex,每次變動,index會加1.而後每一個event都會關聯到currentIndex.
當客戶端調用watch接口(參數中增長 wait參數)時,若是請求參數中有waitIndex,而且waitIndex 小於 currentIndex,則從 EventHistroy 表中查詢index小於等於waitIndex,而且和watch key 匹配的 event,若是有數據,則直接返回。若是歷史表中沒有或者請求沒有帶 waitIndex,則放入WatchHub中,每一個key會關聯一個watcher列表。 當有變動操做時,變動生成的event會放入EventHistroy表中,同時通知和該key相關的watcher。
這裏有幾個影響使用的細節問題:
從而能夠看出,Etcd v2 的一些限制:
Etcd v3 將watch和store拆開實現,咱們先分析下store的實現。
Etcd v3 store 分爲兩部分,一部分是內存中的索引,kvindex,是基於google開源的一個golang的btree實現的,另一部分是後端存儲。按照它的設計,backend能夠對接多種存儲,當前使用的boltdb。boltdb是一個單機的支持事務的kv存儲,Etcd 的事務是基於boltdb的事務實現的。Etcd 在boltdb中存儲的key是reversion,value是 Etcd 本身的key-value組合,也就是說 Etcd 會在boltdb中把每一個版本都保存下,從而實現了多版本機制。
舉個例子: 用etcdctl經過批量接口寫入兩條記錄:
etcdctl txn <<<'
put key1 "v1"
put key2 "v2"
'
再經過批量接口更新這兩條記錄:
etcdctl txn <<<'
put key1 "v12"
put key2 "v22"
'
boltdb中其實有了4條數據:
rev={3 0}, key=key1, value="v1"
rev={3 1}, key=key2, value="v2"
rev={4 0}, key=key1, value="v12"
rev={4 1}, key=key2, value="v22"
reversion主要由兩部分組成,第一部分main rev,每次事務進行加一,第二部分sub rev,同一個事務中的每次操做加一。如上示例,第一次操做的main rev是3,第二次是4。固然這種機制你們想到的第一個問題就是空間問題,因此 Etcd 提供了命令和設置選項來控制compact,同時支持put操做的參數來精確控制某個key的歷史版本數。
瞭解了 Etcd 的磁盤存儲,能夠看出若是要從boltdb中查詢數據,必須經過reversion,但客戶端都是經過key來查詢value,因此 Etcd 的內存kvindex保存的就是key和reversion以前的映射關係,用來加速查詢。
而後咱們再分析下watch機制的實現。Etcd v3 的watch機制支持watch某個固定的key,也支持watch一個範圍(能夠用於模擬目錄的結構的watch),因此 watchGroup 包含兩種watcher,一種是 key watchers,數據結構是每一個key對應一組watcher,另一種是 range watchers, 數據結構是一個 IntervalTree(不熟悉的參看文文末連接),方便經過區間查找到對應的watcher。
同時,每一個 WatchableStore 包含兩種 watcherGroup,一種是synced,一種是unsynced,前者表示該group的watcher數據都已經同步完畢,在等待新的變動,後者表示該group的watcher數據同步落後於當前最新變動,還在追趕。
當 Etcd 收到客戶端的watch請求,若是請求攜帶了revision參數,則比較請求的revision和store當前的revision,若是大於當前revision,則放入synced組中,不然放入unsynced組。同時 Etcd 會啓動一個後臺的goroutine持續同步unsynced的watcher,而後將其遷移到synced組。也就是這種機制下,Etcd v3 支持從任意版本開始watch,沒有v2的1000條歷史event表限制的問題(固然這是指沒有compact的狀況下)。
另外咱們前面提到的,Etcd v2在通知客戶端時,若是網絡很差或者客戶端讀取比較慢,發生了阻塞,則會直接關閉當前鏈接,客戶端須要從新發起請求。Etcd v3爲了解決這個問題,專門維護了一個推送時阻塞的watcher隊列,在另外的goroutine裏進行重試。
Etcd v3 對過時機制也作了改進,過時時間設置在lease上,而後key和lease關聯。這樣能夠實現多個key關聯同一個lease id,方便設置統一的過時時間,以及實現批量續約。
相比Etcd v2, Etcd v3的一些主要變化:
這三個產品是常常被人拿來作選型比較的。 Etcd 和 Zookeeper 提供的能力很是類似,都是通用的一致性元信息存儲,都提供watch機制用於變動通知和分發,也都被分佈式系統用來做爲共享信息存儲,在軟件生態中所處的位置也幾乎是同樣的,能夠互相替代的。兩者除了實現細節,語言,一致性協議上的區別,最大的區別在周邊生態圈。Zookeeper 是apache下的,用java寫的,提供rpc接口,最先從hadoop項目中孵化出來,在分佈式系統中獲得普遍使用(hadoop, solr, kafka, mesos 等)。Etcd 是coreos公司旗下的開源產品,比較新,以其簡單好用的rest接口以及活躍的社區俘獲了一批用戶,在新的一些集羣中獲得使用(好比kubernetes)。雖然v3爲了性能也改爲二進制rpc接口了,但其易用性上比 Zookeeper 仍是好一些。 而 Consul 的目標則更爲具體一些,Etcd 和 Zookeeper 提供的是分佈式一致性存儲能力,具體的業務場景須要用戶本身實現,好比服務發現,好比配置變動。而Consul 則以服務發現和配置變動爲主要目標,同時附帶了kv存儲。 在軟件生態中,越抽象的組件適用範圍越廣,但同時對具體業務場景需求的知足上確定有不足之處。