ETCD是用於共享配置和服務發現的分佈式,一致性的KV存儲系統。該項目目前最新穩定版本爲2.3.0. 具體信息請參考[項目首頁]和[Github]。ETCD是CoreOS公司發起的一個開源項目,受權協議爲Apache。golang
提供配置共享和服務發現的系統比較多,其中最爲你們熟知的是[Zookeeper](後文簡稱ZK),而ETCD能夠算得上是後起之秀了。在項目實現,一致性協議易理解性,運維,安全等多個維度上,ETCD相比Zookeeper都佔據優點。算法
本文選取ZK做爲典型表明與ETCD進行比較,而不考慮[Consul]項目做爲比較對象,緣由爲Consul的可靠性和穩定性還須要時間來驗證(項目發起方自身服務並未使用Consul, 本身都不用)。docker
和ZK相似,ETCD有不少使用場景,包括:數組
按照官網給出的[Benchmark], 在2CPU,1.8G內存,SSD磁盤這樣的配置下,單節點的寫性能能夠達到16K QPS, 而先寫後讀也能達到12K QPS。這個性能仍是至關可觀的。安全
ETCD使用Raft協議來維護集羣內各個節點狀態的一致性。簡單說,ETCD集羣是一個分佈式系統,由多個節點相互通訊構成總體對外服務,每一個節點都存儲了完整的數據,而且經過Raft協議保證每一個節點維護的數據是一致的。
如圖所示,每一個ETCD節點都維護了一個狀態機,而且,任意時刻至多存在一個有效的主節點。主節點處理全部來自客戶端寫操做,經過Raft協議保證寫操做對狀態機的改動會可靠的同步到其餘節點。服務器
ETCD工做原理核心部分在於Raft協議。本節接下來將簡要介紹Raft協議,具體細節請參考其[論文]。
Raft協議正如論文所述,確實方便理解。主要分爲三個部分:選主,日誌複製,安全性。網絡
Raft協議是用於維護一組服務節點數據一致性的協議。這一組服務節點構成一個集羣,而且有一個主節點來對外提供服務。當集羣初始化,或者主節點掛掉後,面臨一個選主問題。集羣中每一個節點,任意時刻處於Leader, Follower, Candidate這三個角色之一。選舉特色以下:數據結構
Candidate節點收到來自主節點的信息後,會當即終止選舉過程,進入Follower角色。架構
爲了不陷入選主失敗循環,每一個節點未收到心跳發起選舉的時間是必定範圍內的隨機值,這樣可以避免2個節點同時發起選主。併發
所謂日誌複製,是指主節點將每次操做造成日誌條目,並持久化到本地磁盤,而後經過網絡IO發送給其餘節點。其餘節點根據日誌的邏輯時鐘(TERM)和日誌編號(INDEX)來判斷是否將該日誌記錄持久化到本地。當主節點收到包括本身在內超過半數節點成功返回,那麼認爲該日誌是可提交的(committed),並將日誌輸入到狀態機,將結果返回給客戶端。
這裏須要注意的是,每次選主都會造成一個惟一的TERM編號,至關於邏輯時鐘。每一條日誌都有全局惟一的編號。
主節點經過網絡IO向其餘節點追加日誌。若某節點收到日誌追加的消息,首先判斷該日誌的TERM是否過時,以及該日誌條目的INDEX是否比當前以及提交的日誌的INDEX跟早。若已過時,或者比提交的日誌更早,那麼就拒絕追加,並返回該節點當前的已提交的日誌的編號。不然,將日誌追加,並返回成功。
當主節點收到其餘節點關於日誌追加的回覆後,若發現有拒絕,則根據該節點返回的已提交日誌編號,發生其編號下一條日誌。
主節點像其餘節點同步日誌,還做了擁塞控制。具體地說,主節點發現日誌複製的目標節點拒絕了某第二天志追加消息,將進入日誌探測階段,一條一條發送日誌,直到目標節點接受日誌,而後進入快速複製階段,可進行批量日誌追加。
按照日誌複製的邏輯,咱們能夠看到,集羣中慢節點不影響整個集羣的性能。另一個特色是,數據只從主節點複製到Follower節點,這樣大大簡化了邏輯流程。
截止此刻,選主以及日誌複製並不能保證節點間數據一致。試想,當一個某個節點掛掉了,一段時間後再次重啓,並當選爲主節點。而在其掛掉這段時間內,集羣如有超過半數節點存活,集羣會正常工做,那麼會有日誌提交。這些提交的日誌沒法傳遞給掛掉的節點。當掛掉的節點再次當選主節點,它將缺失部分已提交的日誌。在這樣場景下,按Raft協議,它將本身日誌複製給其餘節點,會將集羣已經提交的日誌給覆蓋掉。
這顯然是不可接受的。
其餘協議解決這個問題的辦法是,新當選的主節點會詢問其餘節點,和本身數據對比,肯定出集羣已提交數據,而後將缺失的數據同步過來。這個方案有明顯缺陷,增長了集羣恢復服務的時間(集羣在選舉階段不可服務),而且增長了協議的複雜度。
Raft解決的辦法是,在選主邏輯中,對可以成爲主的節點加以限制,確保選出的節點已定包含了集羣已經提交的全部日誌。若是新選出的主節點已經包含了集羣全部提交的日誌,那就不須要從和其餘節點比對數據了。簡化了流程,縮短了集羣恢復服務的時間。
這裏存在一個問題,加以這樣限制以後,還可否選出主呢?答案是:只要仍然有超過半數節點存活,這樣的主必定可以選出。由於已經提交的日誌必然被集羣中超過半數節點持久化,顯然前一個主節點提交的最後一條日誌也被集羣中大部分節點持久化。當主節點掛掉後,集羣中仍有大部分節點存活,那這存活的節點中必定存在一個節點包含了已經提交的日誌了。
至此,關於Raft協議的簡介就所有結束了。
據公開資料顯示,至少有CoreOS, Google Kubernetes, Cloud Foundry, 以及在Github上超過500個項目在使用ETCD。
ETCD提供HTTP協議,在最新版本中支持Google gRPC方式訪問。具體支持接口狀況以下:
本文對ETCD做了一個簡單的介紹,但願對你有幫助。
https://yq.aliyun.com/articles/11035
想必不少人都知道ZooKeeper,一般用做配置共享和服務發現。和它相似,ETCD算是一個很是優秀的後起之秀了。本文重點不在描述他們之間的不一樣點。首先,看看其官網關於ETCD的描述1:
A distributed, reliable key-value store for the most critical data of a distributed system.
在雲計算大行其道的今天,ETCD有不少典型的使用場景。常言道,熟悉一個系統先從部署開始。本文接下來將描述,如何部署ETCD集羣。
安裝官網說明文檔,提供了3種集羣啓動方式,實際上按照其實現原理分爲2類:
在部署集羣以前,咱們須要考慮集羣須要配置多少個節點。這是一個重要的考量,不得忽略。
ETCD使用RAFT協議保證各個節點之間的狀態一致。根據RAFT算法原理,節點數目越多,會下降集羣的寫性能。這是由於每一次寫操做,須要集羣中大多數節點將日誌落盤成功後,Leader節點才能將修改內部狀態機,並返回將結果返回給客戶端。
也就是說在等同配置下,節點數越少,集羣性能越好。顯然,只部署1個節點是沒什麼意義的。一般,按照需求將集羣節點部署爲3,5,7,9個節點。
這裏能選擇偶數個節點嗎? 最好不要這樣。緣由有二:
當網絡分割後,ETCD集羣如何處理的呢?
當網絡分割恢復後,少數派的節點會接受集羣Leader的日誌,直到和其餘節點狀態一致。
這裏只列舉一些重要的參數,以及其用途。
按照官網中的文檔,便可完成集羣啓動。這裏略。
ETCD還提供了另一種啓動方式,即經過服務發現的方式啓動。這種啓動方式,依賴另一個ETCD集羣,在該集羣中建立一個目錄,並在該目錄中建立一個_config
的子目錄,而且在該子目錄中增長一個size
節點,指定集羣的節點數目。
在這種狀況下,將該目錄在ETCD中的URL做爲節點的啓動參數,便可完成集羣啓動。使用--discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
配置項取代靜態配置方式中的--initial-cluster
和inital-cluster-state
參數。其中https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
是在依賴etcd中建立好的目錄url。
在生產環境中,不可避免遇到機器硬件故障。當遇到硬件故障發生的時候,咱們須要快速恢復節點。ETCD集羣能夠作到在不丟失數據的,而且不改變節點ID的狀況下,遷移節點。
具體辦法是:
本文記錄了ETCD集羣啓動的一些注意事項,但願對你有幫助。
https://yq.aliyun.com/articles/29897?spm=5176.100239.blogcont11035.15.7bihps
在理清ETCD的各個模塊的實現細節後,方便線上運維,理解各類參數組合的意義。本文先從網絡層入手,後續文章會依次介紹各個模塊的實現。
本文將着重介紹ETCD服務的網絡層實現細節。在目前的實現中,ETCD經過HTTP協議對外提供服務,一樣經過HTTP協議實現集羣節點間數據交互。
網絡層的主要功能是實現了服務器與客戶端(能發出HTTP請求的各類程序)消息交互,以及集羣內部各節點之間的消息交互。
ETCD-SERVER 大致上能夠分爲網絡層,Raft模塊,複製狀態機,存儲模塊,架構圖如圖1所示。
圖1 ETCD-SERVER架構圖
各個節點在任什麼時候候都有可能變成Leader, Follower, Candidate等角色,同時爲了減小建立連接開銷,ETCD節點在啓動之初就建立了和集羣其餘節點之間的連接。
所以,ETCD集羣節點之間的網絡拓撲是一個任意2個節點之間均有長連接相互鏈接的網狀結構。如圖2所示。
圖2 ETCD集羣節點網絡拓撲圖
須要注意的是,每個節點都會建立到其餘各個節點之間的長連接。每一個節點會向其餘節點宣告本身監聽的端口,該端口只接受來自其餘節點建立連接的請求。
在ETCD實現中,根據不一樣用途,定義了各類不一樣的消息類型。各類不一樣的消息,最終都經過google protocol buffer協議進行封裝。這些消息攜帶的數據大小可能不盡相同。例如 傳輸SNAPSHOT數據的消息數據量就比較大,甚至超過1GB, 而leader到follower節點之間的心跳消息可能只有幾十個字節。
所以,網絡層必須可以高效地處理不一樣數據量的消息。ETCD在實現中,對這些消息採起了分類處理,抽象出了2種類型消息傳輸通道:Stream類型通道和Pipeline類型通道。這兩種消息傳輸通道都使用HTTP協議傳輸數據。
圖3 節點之間創建消息傳輸通道
集羣啓動之初,就建立了這兩種傳輸通道,各自特色:
若是非要作作一個類別的話,Stream就向點與點之間維護了雙向傳輸帶,消息打包後,放到傳輸帶上,傳到對方,對方將回復消息打包放到反向傳輸帶上;而Pipeline就像擁有N輛汽車,大消息打包放到汽車上,開到對端,而後在回來,最多能夠同時發送N個消息。
Stream類型通道
Stream類型通道處理數據量少的消息,例如心跳,日誌追加消息。點到點之間只維護1個HTTP長連接,交替向連接中寫入數據,讀取數據。
Stream 類型通道是節點啓動後主動與其餘每個節點創建。Stream類型通道經過Channel 與Raft模塊傳遞消息。每個Stream類型通道關聯2個Goroutines, 其中一個用於創建HTTP連接,並從連接上讀取數據, decode成message, 經過Channel傳給Raft模塊中,另一個經過Channel 從Raft模塊中收取消息,而後寫入通道。
具體點,ETCD使用golang的http包實現Stream類型通道:
Pipeline類型通道
Pipeline類型通道處理數量大消息,例如SNAPSHOT消息。這種類型消息須要和心跳等消息分開處理,不然會阻塞心跳。
Pipeline類型通道也能夠傳輸小數據量的消息,當且僅當Stream類型連接不可用時。
Pipeline類型通道可用並行發出多個消息,維護一組Goroutines, 每個Goroutines均可向對端發出POST請求(攜帶數據),收到回覆後,連接關閉。
具體地,ETCD使用golang的http包實現的:
在ETCD中,Raft協議被抽象爲Raft模塊。按照Raft協議,節點之間須要交互數據。在ETCD中,經過Raft模塊中抽象的RaftNode擁有一個message box, RaftNode將各類類型消息放入到messagebox中,有專門Goroutine將box裏的消息寫入管道,而管道的另一端就連接在網絡層的不一樣類型的傳輸通道上,有專門的Goroutine在等待(select)。
而網絡層收到的消息,也經過管道傳給RaftNode。RaftNode中有專門的Goroutine在等待消息。
也就是說,網絡層與Raft模塊之間經過Golang Channel完成數據通訊。這個比較容易理解。
在ETCD-SERVER啓動之初,會監聽服務端口,當服務端口收到請求後,解析出message後,經過管道傳入給Raft模塊,當Raft模塊按照Raft協議完成操做後,回覆該請求(或者請求超時關閉了)。
網絡層抽象爲Transport類,該類完成網絡數據收發。對Raft模塊提供Send/SendSnapshot接口,提供數據讀寫的Channel,對外監聽指定端口。
本文整理了ETCD節點網絡層的實現,爲分析其餘模塊打下基礎。
https://yq.aliyun.com/articles/29900?spm=5176.100239.blogcont29897.12.4qqzpF