ETCD相關介紹--總體概念及原理方面

etcd做爲一個受到ZooKeeper與doozer啓發而催生的項目,除了擁有與之相似的功能外,更專一於如下四點。

  1. 簡單:基於HTTP+JSON的API讓你用curl就能夠輕鬆使用。
  2. 安全:可選SSL客戶認證機制。
  3. 快速:每一個實例每秒支持一千次寫操做。
  4. 可信:使用Raft算法充分實現了分佈式。


分佈式系統中的數據分爲控制數據和應用數據。etcd的使用場景默認處理的數據都是控制數據,對於應用數據,只推薦數據量很小,可是更新訪問頻繁的狀況。

應用場景有以下幾類:
場景一:服務發現(Service Discovery)
場景二:消息發佈與訂閱
場景三:負載均衡
場景四:分佈式通知與協調
場景五:分佈式鎖、分佈式隊列
場景六:集羣監控與Leader競選

舉個最簡單的例子,若是你須要一個分佈式存儲倉庫來存儲配置信息,而且但願這個倉庫讀寫速度快、支持高可用、部署簡單、支持http接口,那麼就可使用etcd。

目前,cloudfoundry使用etcd做爲hm9000的應用狀態信息存儲,kubernetes用etcd來存儲docker集羣的配置信息等。

更爲詳盡的內容可閱讀etcd:從應用場景到實現原理的全方位解讀一文。

http://dockone.io/question/115

 

1. ETCD是什麼

ETCD是用於共享配置和服務發現的分佈式,一致性的KV存儲系統。該項目目前最新穩定版本爲2.3.0. 具體信息請參考[項目首頁]和[Github]。ETCD是CoreOS公司發起的一個開源項目,受權協議爲Apache。golang

DraggedImage

提供配置共享和服務發現的系統比較多,其中最爲你們熟知的是[Zookeeper](後文簡稱ZK),而ETCD能夠算得上是後起之秀了。在項目實現,一致性協議易理解性,運維,安全等多個維度上,ETCD相比Zookeeper都佔據優點。算法

2. ETCD vs ZK

本文選取ZK做爲典型表明與ETCD進行比較,而不考慮[Consul]項目做爲比較對象,緣由爲Consul的可靠性和穩定性還須要時間來驗證(項目發起方自身服務並未使用Consul, 本身都不用)。docker

  • 一致性協議: ETCD使用[Raft]協議, ZK使用ZAB(類PAXOS協議),前者容易理解,方便工程實現;
  • 運維方面:ETCD方便運維,ZK難以運維;
  • 項目活躍度:ETCD社區與開發活躍,ZK已經快死了;
  • API:ETCD提供HTTP+JSON, gRPC接口,跨平臺跨語言,ZK須要使用其客戶端;
  • 訪問安全方面:ETCD支持HTTPS訪問,ZK在這方面缺失;

3. ETCD的使用場景

和ZK相似,ETCD有不少使用場景,包括:數組

  • 配置管理
  • 服務註冊於發現
  • 選主
  • 應用調度
  • 分佈式隊列
  • 分佈式鎖

4. ETCD讀寫性能

按照官網給出的[Benchmark], 在2CPU,1.8G內存,SSD磁盤這樣的配置下,單節點的寫性能能夠達到16K QPS, 而先寫後讀也能達到12K QPS。這個性能仍是至關可觀的。安全

5. ETCD工做原理

ETCD使用Raft協議來維護集羣內各個節點狀態的一致性。簡單說,ETCD集羣是一個分佈式系統,由多個節點相互通訊構成總體對外服務,每一個節點都存儲了完整的數據,而且經過Raft協議保證每一個節點維護的數據是一致的。
DraggedImage_1
如圖所示,每一個ETCD節點都維護了一個狀態機,而且,任意時刻至多存在一個有效的主節點。主節點處理全部來自客戶端寫操做,經過Raft協議保證寫操做對狀態機的改動會可靠的同步到其餘節點。服務器

ETCD工做原理核心部分在於Raft協議。本節接下來將簡要介紹Raft協議,具體細節請參考其[論文]。
Raft協議正如論文所述,確實方便理解。主要分爲三個部分:選主,日誌複製,安全性。網絡

5.1 選主

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

  • 當集羣初始化時候,每一個節點都是Follower角色;
  • 集羣中存在至多1個有效的主節點,經過心跳與其餘節點同步數據;
  • 當Follower在必定時間內沒有收到來自主節點的心跳,會將本身角色改變爲Candidate,併發起一次選主投票;當收到包括本身在內超過半數節點同意後,選舉成功;當收到票數不足半數選舉失敗,或者選舉超時。若本輪未選出主節點,將進行下一輪選舉(出現這種狀況,是因爲多個節點同時選舉,全部節點均爲得到過半選票)。
  • Candidate節點收到來自主節點的信息後,會當即終止選舉過程,進入Follower角色。架構

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

5.2 日誌複製

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

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

DraggedImage_2

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

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

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

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

5.3 安全性

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

這顯然是不可接受的。

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

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

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

至此,關於Raft協議的簡介就所有結束了。

6. ETCD使用案例

據公開資料顯示,至少有CoreOS, Google Kubernetes, Cloud Foundry, 以及在Github上超過500個項目在使用ETCD。

7. ETCD接口

ETCD提供HTTP協議,在最新版本中支持Google gRPC方式訪問。具體支持接口狀況以下:

  • ETCD是一個高可靠的KV存儲系統,支持PUT/GET/DELETE接口;
  • 爲了支持服務註冊與發現,支持WATCH接口(經過http long poll實現);
  • 支持KEY持有TTL屬性;
  • CAS(compare and swap)操做;
  • 支持多key的事務操做;
  • 支持目錄操做

8. 結束

本文對ETCD做了一個簡單的介紹,但願對你有幫助。

https://yq.aliyun.com/articles/11035

ETCD系列之二:部署集羣

1. 概述

想必不少人都知道ZooKeeper,一般用做配置共享和服務發現。和它相似,ETCD算是一個很是優秀的後起之秀了。本文重點不在描述他們之間的不一樣點。首先,看看其官網關於ETCD的描述1:

A distributed, reliable key-value store for the most critical data of a distributed system.

在雲計算大行其道的今天,ETCD有不少典型的使用場景。常言道,熟悉一個系統先從部署開始。本文接下來將描述,如何部署ETCD集羣。

安裝官網說明文檔,提供了3種集羣啓動方式,實際上按照其實現原理分爲2類:

  • 經過靜態配置方式啓動
  • 經過服務發現方式啓動

在部署集羣以前,咱們須要考慮集羣須要配置多少個節點。這是一個重要的考量,不得忽略。

2. 集羣節點數量與網絡分割

ETCD使用RAFT協議保證各個節點之間的狀態一致。根據RAFT算法原理,節點數目越多,會下降集羣的寫性能。這是由於每一次寫操做,須要集羣中大多數節點將日誌落盤成功後,Leader節點才能將修改內部狀態機,並返回將結果返回給客戶端。

也就是說在等同配置下,節點數越少,集羣性能越好。顯然,只部署1個節點是沒什麼意義的。一般,按照需求將集羣節點部署爲3,5,7,9個節點。

這裏能選擇偶數個節點嗎? 最好不要這樣。緣由有二:

  • 偶數個節點集羣不可用風險更高,表如今選主過程當中,有較大機率或等額選票,從而觸發下一輪選舉。
  • 偶數個節點集羣在某些網絡分割的場景下沒法正常工做。試想,當網絡分割發生後,將集羣節點對半分割開。此時集羣將沒法工做。按照RAFT協議,此時集羣寫操做沒法使得大多數節點贊成,從而致使寫失敗,集羣沒法正常工做。

當網絡分割後,ETCD集羣如何處理的呢?

  • 當集羣的Leader在多數節點這一側時,集羣仍能夠正常工做。少數節點那一側沒法收到Leader心跳,也沒法完成選舉。
  • 當集羣的Leader在少數節點這一側時,集羣仍能夠正常工做,多數派的節點可以選出新的Leader, 集羣服務正常進行。

當網絡分割恢復後,少數派的節點會接受集羣Leader的日誌,直到和其餘節點狀態一致。

3. ETCD參數說明

這裏只列舉一些重要的參數,以及其用途。

  • —data-dir 指定節點的數據存儲目錄,這些數據包括節點ID,集羣ID,集羣初始化配置,Snapshot文件,若未指定—wal-dir,還會存儲WAL文件;
  • —wal-dir 指定節點的was文件的存儲目錄,若指定了該參數,wal文件會和其餘數據文件分開存儲。
  • —name 節點名稱
  • —initial-advertise-peer-urls 告知集羣其餘節點url.
  • — listen-peer-urls 監聽URL,用於與其餘節點通信
  • — advertise-client-urls 告知客戶端url, 也就是服務的url
  • — initial-cluster-token 集羣的ID
  • — initial-cluster 集羣中全部節點

4. 經過靜態配置方式啓動ETCD集羣

按照官網中的文檔,便可完成集羣啓動。這裏略。

5. 經過服務發現方式啓動ETCD集羣

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。

6. 節點遷移

在生產環境中,不可避免遇到機器硬件故障。當遇到硬件故障發生的時候,咱們須要快速恢復節點。ETCD集羣能夠作到在不丟失數據的,而且不改變節點ID的狀況下,遷移節點。
具體辦法是:

  • 1)中止待遷移節點上的etc進程;
  • 2)將數據目錄打包複製到新的節點;
  • 3)更新該節點對應集羣中peer url,讓其指向新的節點;
  • 4)使用相同的配置,在新的節點上啓動etcd進程;

5. 結束

本文記錄了ETCD集羣啓動的一些注意事項,但願對你有幫助。

https://yq.aliyun.com/articles/29897?spm=5176.100239.blogcont11035.15.7bihps

ETCD系列之三:網絡層實現

1. 概述

在理清ETCD的各個模塊的實現細節後,方便線上運維,理解各類參數組合的意義。本文先從網絡層入手,後續文章會依次介紹各個模塊的實現。

本文將着重介紹ETCD服務的網絡層實現細節。在目前的實現中,ETCD經過HTTP協議對外提供服務,一樣經過HTTP協議實現集羣節點間數據交互。

網絡層的主要功能是實現了服務器與客戶端(能發出HTTP請求的各類程序)消息交互,以及集羣內部各節點之間的消息交互。

2. ETCD-SERVER總體架構

ETCD-SERVER 大致上能夠分爲網絡層,Raft模塊,複製狀態機,存儲模塊,架構圖如圖1所示。
thread_local_impl_2_
圖1 ETCD-SERVER架構圖

  • 網絡層:提供網絡數據讀寫功能,監聽服務端口,完成集羣節點之間數據通訊,收發客戶端數據;
  • Raft模塊:完整實現了Raft協議;
  • 存儲模塊:KV存儲,WAL文件,SNAPSHOT管理
  • 複製狀態機:這個是一個抽象的模塊,狀態機的數據維護在內存中,按期持久化到磁盤,每次寫請求會持久化到WAL文件,並根據寫請求的內容修改狀態機數據。 ## 3. 節點之間網絡拓撲結構 ETCD集羣的各個節點之間須要經過HTTP協議來傳遞數據,表如今:
  • Leader 向Follower發送心跳包, Follower向Leader回覆消息;
  • Leader向Follower發送日誌追加信息;
  • Leader向Follower發送Snapshot數據;
  • Candidate節點發起選舉,向其餘節點發起投票請求;
  • Follower將收的寫操做轉發給Leader;

各個節點在任什麼時候候都有可能變成Leader, Follower, Candidate等角色,同時爲了減小建立連接開銷,ETCD節點在啓動之初就建立了和集羣其餘節點之間的連接。

所以,ETCD集羣節點之間的網絡拓撲是一個任意2個節點之間均有長連接相互鏈接的網狀結構。如圖2所示。
thread_local_impl
圖2 ETCD集羣節點網絡拓撲圖

須要注意的是,每個節點都會建立到其餘各個節點之間的長連接。每一個節點會向其餘節點宣告本身監聽的端口,該端口只接受來自其餘節點建立連接的請求。

4. 節點之間消息交互

在ETCD實現中,根據不一樣用途,定義了各類不一樣的消息類型。各類不一樣的消息,最終都經過google protocol buffer協議進行封裝。這些消息攜帶的數據大小可能不盡相同。例如 傳輸SNAPSHOT數據的消息數據量就比較大,甚至超過1GB, 而leader到follower節點之間的心跳消息可能只有幾十個字節。

所以,網絡層必須可以高效地處理不一樣數據量的消息。ETCD在實現中,對這些消息採起了分類處理,抽象出了2種類型消息傳輸通道:Stream類型通道和Pipeline類型通道。這兩種消息傳輸通道都使用HTTP協議傳輸數據。
thread_local_impl_1_
圖3 節點之間創建消息傳輸通道

集羣啓動之初,就建立了這兩種傳輸通道,各自特色:

  • Stream類型通道:點到點之間維護HTTP長連接,主要用於傳輸數據量較小的消息,例如追加日誌,心跳等;
  • Pipeline類型通道:點到點之間不維護HTTP長連接,短連接傳輸數據,用完即關閉。用於傳輸數據量大的消息,例如snapshot數據。

若是非要作作一個類別的話,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類型通道:

  • 1)被動發起方監聽端口, 並在對應的url上掛載相應的handler(當前請求來領時,handler的ServeHTTP方法會被調用)
  • 2)主動發起方發送HTTP GET請求;
  • 3)監聽方的Handler的ServeHTTP訪問被調用(框架層傳入http.ResponseWriter和http.Request對象),其中http.ResponseWriter對象做爲參數傳入Writter-Goroutine(就這麼稱呼吧),該Goroutine的主循環就是將Raft模塊傳出的message寫入到這個responseWriter對象裏;http.Request的成員變量Body傳入到Reader-Gorouting(就這麼稱呼吧),該Gorutine的主循環就是不斷讀取Body上的數據,decode成message 經過Channel傳給Raft模塊。

Pipeline類型通道
Pipeline類型通道處理數量大消息,例如SNAPSHOT消息。這種類型消息須要和心跳等消息分開處理,不然會阻塞心跳。

Pipeline類型通道也能夠傳輸小數據量的消息,當且僅當Stream類型連接不可用時。

Pipeline類型通道可用並行發出多個消息,維護一組Goroutines, 每個Goroutines均可向對端發出POST請求(攜帶數據),收到回覆後,連接關閉。

具體地,ETCD使用golang的http包實現的:

  • 1)根據參數配置,啓動N個Goroutines;
  • 2)每個Goroutines的主循環阻塞在消息Channel上,當收到消息後,經過POST請求發出數據,並等待回覆。

5. 網絡層與Raft模塊之間的交互

在ETCD中,Raft協議被抽象爲Raft模塊。按照Raft協議,節點之間須要交互數據。在ETCD中,經過Raft模塊中抽象的RaftNode擁有一個message box, RaftNode將各類類型消息放入到messagebox中,有專門Goroutine將box裏的消息寫入管道,而管道的另一端就連接在網絡層的不一樣類型的傳輸通道上,有專門的Goroutine在等待(select)。

而網絡層收到的消息,也經過管道傳給RaftNode。RaftNode中有專門的Goroutine在等待消息。

也就是說,網絡層與Raft模塊之間經過Golang Channel完成數據通訊。這個比較容易理解。

6 ETCD-SERVER處理請求(與客戶端的信息交互)

在ETCD-SERVER啓動之初,會監聽服務端口,當服務端口收到請求後,解析出message後,經過管道傳入給Raft模塊,當Raft模塊按照Raft協議完成操做後,回覆該請求(或者請求超時關閉了)。

7 主要數據結構

網絡層抽象爲Transport類,該類完成網絡數據收發。對Raft模塊提供Send/SendSnapshot接口,提供數據讀寫的Channel,對外監聽指定端口。

thread_local_impl_3_

8. 結束

本文整理了ETCD節點網絡層的實現,爲分析其餘模塊打下基礎。

https://yq.aliyun.com/articles/29900?spm=5176.100239.blogcont29897.12.4qqzpF

相關文章
相關標籤/搜索