1.什么是etcd服務
etcd是一個采用HTTP協(xié)議的健/值對存儲系統(tǒng),它是一個分布式和功能層次配置系統(tǒng)紧显,可用于構建服務發(fā)現(xiàn)系統(tǒng)。用于共享配置和服務發(fā)現(xiàn)的分布式,一致性的KV存儲系統(tǒng).其很容易部署缕棵、安裝和使用孵班,提供了可靠的數(shù)據(jù)持久化特性涉兽。它是安全的并且文檔也十分齊全。
ETCD該項目目前最新穩(wěn)定版本為3.3.9 具體信息請參考[項目首頁]和[Github]篙程。ETCD是CoreOS公司發(fā)起的一個開源項目枷畏,授權協(xié)議為Apache.
提供配置共享和服務發(fā)現(xiàn)的系統(tǒng)比較多,其中最為大家熟知的是Zookeeper虱饿,而ETCD可以算得上是后起之秀了拥诡。在項目實現(xiàn),一致性協(xié)議易理解性郭厌,運維,安全等多個維度上雕蔽,ETCD相比Zookeeper都占據(jù)優(yōu)勢
2.Zookeeper和etcd的區(qū)別:
1)一致性協(xié)議: ETCD使用Raft協(xié)議折柠, .Zookeeper使用ZAB(類PAXOS協(xié)議),前者容易理解批狐,方便工程實現(xiàn)扇售;
2)運維方面:ETCD方便運維,ZK難以運維嚣艇;
3)項目活躍度:ETCD社區(qū)與開發(fā)活躍承冰,ZK已經(jīng)快死了;
4)API:ETCD提供HTTP+JSON, gRPC接口食零,跨平臺跨語言困乒,ZK需要使用其客戶端;
5)訪問安全方面:ETCD支持HTTPS訪問贰谣,ZK在這方面缺失娜搂;
3.etcd的應用場景:
配置管理,服務注冊于發(fā)現(xiàn)吱抚,選主百宇,應用調(diào)度,分布式隊列秘豹,分布式鎖
4.Etcd 主要提供以下能力:
1)提供存儲以及獲取數(shù)據(jù)的接口携御,它通過協(xié)議保證 Etcd 集群中的多個節(jié)點數(shù)據(jù)的強一致性。用于存儲元信息以及共享配置既绕。
2)提供監(jiān)聽機制啄刹,客戶端可以監(jiān)聽某個key或者某些key的變更(v2和v3的機制不同,參看后面文章)凄贩。用于監(jiān)聽和推送變更鸵膏。
3)提供key的過期以及續(xù)約機制,客戶端通過定時刷新來實現(xiàn)續(xù)約(v2和v3的實現(xiàn)機制也不一樣)怎炊。用于集群監(jiān)控以及服務注冊發(fā)現(xiàn)谭企。
4)提供原子的CAS(Compare-and-Swap)和 CAD(Compare-and-Delete)支持(v2通過接口參數(shù)實現(xiàn)廓译,v3通過批量事務實現(xiàn))。用于分布式鎖以及l(fā)eader選舉债查。
5.etcd的工作原理:
ETCD使用Raft協(xié)議來維護集群內(nèi)各個節(jié)點狀態(tài)的一致性非区。簡單說,ETCD集群是一個分布式系統(tǒng)盹廷,由多個節(jié)點相互通信構成整體對外服務征绸,每個節(jié)點都存儲了完整的數(shù)據(jù),并且通過Raft協(xié)議保證每個節(jié)點維護的數(shù)據(jù)是一致的俄占。
如圖所示管怠,每個ETCD節(jié)點都維護了一個狀態(tài)機,并且缸榄,任意時刻至多存在一個有效的主節(jié)點渤弛。主節(jié)點處理所有來自客戶端寫操作,通過Raft協(xié)議保證寫操作對狀態(tài)機的改動會可靠的同步到其他節(jié)點甚带。
6. 集群節(jié)點數(shù)量
ETCD使用RAFT協(xié)議保證各個節(jié)點之間的狀態(tài)一致她肯。根據(jù)RAFT算法原理,節(jié)點數(shù)目越多鹰贵,會降低集群的寫性能晴氨。這是因為每一次寫操作,需要集群中大多數(shù)節(jié)點將日志落盤成功后碉输,Leader節(jié)點才能將修改內(nèi)部狀態(tài)機籽前,并返回將結果返回給客戶端。
也就是說在等同配置下敷钾,節(jié)點數(shù)越少聚假,集群性能越好。顯然闰非,只部署1個節(jié)點是沒什么意義的膘格。通常,按照需求將集群節(jié)點部署為3财松,5瘪贱,7,9個節(jié)點辆毡。
這里能選擇偶數(shù)個節(jié)點嗎菜秦? 最好不要這樣。原因有二:
1)偶數(shù)個節(jié)點集群不可用風險更高舶掖,表現(xiàn)在選主過程中球昨,有較大概率或等額選票,從而觸發(fā)下一輪選舉眨攘。
2)偶數(shù)個節(jié)點集群在某些網(wǎng)絡分割的場景下無法正常工作主慰。試想嚣州,當網(wǎng)絡分割發(fā)生后,將集群節(jié)點對半分割開共螺。此時集群將無法工作该肴。按照RAFT協(xié)議,此時集群寫操作無法使得大多數(shù)節(jié)點同意藐不,從而導致寫失敗匀哄,集群無法正常工作。
7.節(jié)點遷移
在生產(chǎn)環(huán)境中雏蛮,不可避免遇到機器硬件故障涎嚼。當遇到硬件故障發(fā)生的時候,我們需要快速恢復節(jié)點挑秉。ETCD集群可以做到在不丟失數(shù)據(jù)的法梯,并且不改變節(jié)點ID的情況下,遷移節(jié)點衷模。
具體辦法是:
1)停止待遷移節(jié)點上的etc進程鹊汛;
2)將數(shù)據(jù)目錄打包復制到新的節(jié)點蒲赂;
3)更新該節(jié)點對應集群中peer url阱冶,讓其指向新的節(jié)點;
4)使用相同的配置滥嘴,在新的節(jié)點上啟動etcd進程木蹬;
8.Etcd v2 與 v3區(qū)別
Etcd v2 和 v3 本質(zhì)上是共享同一套 raft 協(xié)議代碼的兩個獨立的應用,接口不一樣若皱,存儲不一樣镊叁,數(shù)據(jù)互相隔離。也就是說如果從 Etcd v2 升級到 Etcd v3走触,原來v2 的數(shù)據(jù)還是只能用 v2 的接口訪問晦譬,v3 的接口創(chuàng)建的數(shù)據(jù)也只能訪問通過 v3 的接口訪問。所以我們按照 v2 和 v3 分別分析:
1)Etcd v2 存儲互广,Watch以及過期機制
Etcd v2 是個純內(nèi)存的實現(xiàn)敛腌,并未實時將數(shù)據(jù)寫入到磁盤,持久化機制很簡單惫皱,就是將store整合序列化成json寫入文件像樊。數(shù)據(jù)在內(nèi)存中是一個簡單的樹結構
當客戶端調(diào)用watch接口(參數(shù)中增加 wait參數(shù))時,如果請求參數(shù)中有waitIndex旅敷,并且waitIndex 小于 currentIndex生棍,則從 EventHistroy 表中查詢index小于等于waitIndex,并且和watch key 匹配的 event媳谁,如果有數(shù)據(jù)涂滴,則直接返回友酱。如果歷史表中沒有或者請求沒有帶 waitIndex,則放入WatchHub中氢妈,每個key會關聯(lián)一個watcher列表粹污。 當有變更操作時,變更生成的event會放入EventHistroy表中首量,同時通知和該key相關的watcher壮吩。
這里有幾個影響使用的細節(jié)問題:
1)EventHistroy 是有長度限制的,最長1000加缘。也就是說鸭叙,如果你的客戶端停了許久,然后重新watch的時候拣宏,可能和該waitIndex相關的event已經(jīng)被淘汰了沈贝,這種情況下會丟失變更。
2)如果通知watch的時候勋乾,出現(xiàn)了阻塞(每個watch的channel有100個緩沖空間)宋下,Etcd 會直接把watcher刪除,也就是會導致wait請求的連接中斷辑莫,客戶端需要重新連接学歧。
3)Etcd store的每個node中都保存了過期時間,通過定時機制進行清理各吨。
從而可以看出枝笨,Etcd v2 的一些限制:
1)過期時間只能設置到每個key上,如果多個key要保證生命周期一致則比較困難揭蜒。
2)watch只能watch某一個key以及其子節(jié)點(通過參數(shù) recursive),不能進行多個watch横浑。
3)很難通過watch機制來實現(xiàn)完整的數(shù)據(jù)同步(有丟失變更的風險),所以當前的大多數(shù)使用方式是通過watch得知變更屉更,然后通過get重新獲取數(shù)據(jù)徙融,并不完全依賴于watch的變更event。
2)Etcd v3 存儲瑰谜,Watch以及過期機制
Etcd v3 將watch和store拆開實現(xiàn)欺冀,我們先分析下store的實現(xiàn)。
Etcd v3 store 分為兩部分似舵,一部分是內(nèi)存中的索引脚猾,kvindex,是基于google開源的一個golang的btree實現(xiàn)的砚哗,另外一部分是后端存儲龙助。按照它的設計,backend可以對接多種存儲,當前使用的boltdb提鸟。boltdb是一個單機的支持事務的kv存儲军援,Etcd 的事務是基于boltdb的事務實現(xiàn)的。Etcd 在boltdb中存儲的key是reversion称勋,value是 Etcd 自己的key-value組合胸哥,也就是說 Etcd 會在boltdb中把每個版本都保存下,從而實現(xiàn)了多版本機制赡鲜。
reversion主要由兩部分組成空厌,第一部分main rev,每次事務進行加一银酬,第二部分sub rev嘲更,同一個事務中的每次操作加一。第一次操作的main rev是3揩瞪,第二次是4赋朦。當然這種機制大家想到的第一個問題就是空間問題,所以 Etcd 提供了命令和設置選項來控制compact李破,同時支持put操作的參數(shù)來精確控制某個key的歷史版本數(shù)宠哄。
了解了 Etcd 的磁盤存儲,可以看出如果要從boltdb中查詢數(shù)據(jù)嗤攻,必須通過reversion毛嫉,但客戶端都是通過key來查詢value,所以 Etcd 的內(nèi)存kvindex保存的就是key和reversion之前的映射關系屯曹,用來加速查詢狱庇。
然后我們再分析下watch機制的實現(xiàn)惊畏。Etcd v3 的watch機制支持watch某個固定的key恶耽,也支持watch一個范圍(可以用于模擬目錄的結構的watch),所以 watchGroup 包含兩種watcher颜启,一種是 key watchers偷俭,數(shù)據(jù)結構是每個key對應一組watcher,另外一種是 range watchers, 數(shù)據(jù)結構是一個 IntervalTree(不熟悉的參看文文末鏈接)缰盏,方便通過區(qū)間查找到對應的watcher涌萤。
同時,每個 WatchableStore 包含兩種 watcherGroup口猜,一種是synced负溪,一種是unsynced,前者表示該group的watcher數(shù)據(jù)都已經(jīng)同步完畢济炎,在等待新的變更川抡,后者表示該group的watcher數(shù)據(jù)同步落后于當前最新變更,還在追趕须尚。
當 Etcd 收到客戶端的watch請求崖堤,如果請求攜帶了revision參數(shù)侍咱,則比較請求的revision和store當前的revision,如果大于當前revision密幔,則放入synced組中楔脯,否則放入unsynced組。同時 Etcd 會啟動一個后臺的goroutine持續(xù)同步unsynced的watcher胯甩,然后將其遷移到synced組昧廷。也就是這種機制下,Etcd v3 支持從任意版本開始watch偎箫,沒有v2的1000條歷史event表限制的問題(當然這是指沒有compact的情況下)麸粮。
另外我們前面提到的,Etcd v2在通知客戶端時镜廉,如果網(wǎng)絡不好或者客戶端讀取比較慢弄诲,發(fā)生了阻塞,則會直接關閉當前連接娇唯,客戶端需要重新發(fā)起請求齐遵。Etcd v3為了解決這個問題,專門維護了一個推送時阻塞的watcher隊列塔插,在另外的goroutine里進行重試梗摇。
Etcd v3 對過期機制也做了改進,過期時間設置在lease上想许,然后key和lease關聯(lián)忍级。這樣可以實現(xiàn)多個key關聯(lián)同一個lease id,方便設置統(tǒng)一的過期時間纺腊,以及實現(xiàn)批量續(xù)約薄霜。
9.相比Etcd v2, Etcd v3的一些主要變化:
1)接口通過grpc提供rpc接口,放棄了v2的http接口漱凝。優(yōu)勢是長連接效率提升明顯疮蹦,缺點是使用不如以前方便,尤其對不方便維護長連接的場景茸炒。
2)廢棄了原來的目錄結構愕乎,變成了純粹的kv,用戶可以通過前綴匹配模式模擬目錄壁公。
3)內(nèi)存中不再保存value感论,同樣的內(nèi)存可以支持存儲更多的key。
4)watch機制更穩(wěn)定紊册,基本上可以通過watch機制實現(xiàn)數(shù)據(jù)的完全同步比肄。
5)(提供了批量操作以及事務機制,用戶可以通過批量事務請求來實現(xiàn)Etcd v2的CAS機制(批量事務支持if條件判斷)
10.Etcd 使用注意事項
1)Etcd cluster 初始化的問題
如果集群第一次初始化啟動的時候,有一臺節(jié)點未啟動薪前,通過v3的接口訪問的時候润努,會報告Error:? Etcdserver: not capable 錯誤。這是為兼容性考慮示括,集群啟動時默認的API版本是2.3铺浇,只有當集群中的所有節(jié)點都加入了,確認所有節(jié)點都支持v3接口時垛膝,才提升集群版本到v3鳍侣。這個只有第一次初始化集群的時候會遇到,如果集群已經(jīng)初始化完畢吼拥,再掛掉節(jié)點倚聚,或者集群關閉重啟(關閉重啟的時候會從持久化數(shù)據(jù)中加載集群API版本),都不會有影響凿可。
2)Etcd 讀請求的機制
v2 ?quorum=true 的時候惑折,讀取是通過raft進行的,通過cli請求枯跑,該參數(shù)默認為true惨驶。
v3 ?–consistency=“l(fā)” 的時候(默認)通過raft讀取,否則讀取本地數(shù)據(jù)敛助。sdk 代碼里則是通過是否打開:WithSerializable option 來控制粗卜。
一致性讀取的情況下,每次讀取也需要走一次raft協(xié)議纳击,能保證一致性续扔,但性能有損失,如果出現(xiàn)網(wǎng)絡分區(qū)焕数,集群的少數(shù)節(jié)點是不能提供一致性讀取的纱昧。但如果不設置該參數(shù),則是直接從本地的store里讀取百匆,這樣就損失了一致性砌些。使用的時候需要注意根據(jù)應用場景設置這個參數(shù)呜投,在一致性和可用性之間進行取舍加匈。
3)Etcd 的 compact 機制
Etcd 默認不會自動 compact,需要設置啟動參數(shù)仑荐,或者通過命令進行compact雕拼,如果變更頻繁建議設置,否則會導致空間和內(nèi)存的浪費以及錯誤粘招。Etcd v3 的默認的 backend quota 2GB啥寇,如果不 compact,boltdb 文件大小超過這個限制后,就會報錯:”Error: etcdserver: mvcc: database space exceeded”辑甜,導致數(shù)據(jù)無法寫入衰絮。