從etcd的架構開始,深刻到源碼中解析etcdnode
從etcd的架構圖中咱們能夠看到,etcd主要分爲四個部分。git
一般,一個用戶的請求發送過來,會經由HTTP Server轉發給Store進行具體的事務處理github
若是涉及到節點的修改,則交給Raft模塊進行狀態的變動、日誌的記錄,而後再同步給別的etcd節點以確認數據提交算法
最後進行數據的提交,再次同步。api
etcd做爲一個高可用鍵值存儲系統,天生就是爲集羣化而設計的。安全
因爲Raft算法在作決策時須要多數節點的投票,因此etcd通常部署集羣推薦奇數個節點,推薦的數量爲三、5或者7個節點構成一個集羣。bash
etcd有三種集羣化啓動的配置方案,分別爲靜態配置啓動、etcd自身服務發現、經過DNS進行服務發現。服務器
經過配置內容的不一樣,你能夠對不一樣的方式進行選擇。值得一提的是,這也是新版etcd區別於舊版的一大特性,它摒棄了使用配置文件進行參數配置的作法,轉而使用命令行參數或者環境變量的作法來配置參數。網絡
這種方式比較適用於離線環境,在啓動整個集羣以前,你就已經預先清楚所要配置的集羣大小,以及集羣上各節點的地址和端口信息。那麼啓動時,你就能夠經過配置initial-cluster
參數進行etcd集羣的啓動。
在每一個etcd機器啓動時,配置環境變量或者添加啓動參數的方式以下。
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380" ETCD_INITIAL_CLUSTER_STATE=new
參數方法:
-initial-cluster infra0=http://10.0.1.10:2380,http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ -initial-cluster-state new
值得注意的是,
-initial-cluster
參數中配置的url地址必須與各個節點啓動時設置的initial-advertise-peer-urls
參數相同。
(initial-advertise-peer-urls
參數表示節點監聽其餘節點同步信號的地址)
若是你所在的網絡環境配置了多個etcd集羣,爲了不意外發生,最好使用-initial-cluster-token
參數爲每一個集羣單獨配置一個token認證。這樣就能夠確保每一個集羣和集羣的成員都擁有獨特的ID。
綜上所述,若是你要配置包含3個etcd節點的集羣,那麼你在三個機器上的啓動命令分別以下所示。
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \ -listen-peer-urls http://10.0.1.10:2380 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ -initial-cluster-state new $ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \ -listen-peer-urls http://10.0.1.11:2380 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ -initial-cluster-state new $ etcd -name infra2 -initial-advertise-peer-urls http://10.0.1.12:2380 \ -listen-peer-urls http://10.0.1.12:2380 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ -initial-cluster-state new
在初始化完成後,etcd還提供動態增、刪、改etcd集羣節點的功能,這個須要用到etcdctl
命令進行操做。
經過自發現的方式啓動etcd集羣須要事先準備一個etcd集羣。若是你已經有一個etcd集羣,首先你能夠執行以下命令設定集羣的大小,假設爲3.
$ curl -X PUT http://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3
而後你要把這個url地址http://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
做爲-discovery
參數來啓動etcd。
節點會自動使用http://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
目錄進行etcd的註冊和發現服務。
因此最終你在某個機器上啓動etcd的命令以下。
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \ -listen-peer-urls http://10.0.1.10:2380 \ -discovery http://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
若是你本地沒有可用的etcd集羣,etcd官網提供了一個能夠公網訪問的etcd存儲地址。你能夠經過以下命令獲得etcd服務的目錄,並把它做爲-discovery
參數使用。
$ curl http://discovery.etcd.io/new?size=3 http://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
一樣的,當你完成了集羣的初始化後,這些信息就失去了做用。
當你須要增長節點時,須要使用etcdctl
來進行操做。
爲了安全,請務必每次啓動新etcd集羣時,都使用新的discovery token進行註冊。
另外,若是你初始化時啓動的節點超過了指定的數量,多餘的節點會自動轉化爲Proxy模式的etcd。
etcd還支持使用DNS SRV記錄進行啓動。關於DNS SRV記錄如何進行服務發現,能夠參閱RFC2782,因此,你要在DNS服務器上進行相應的配置。
(1) 開啓DNS服務器上SRV記錄查詢,並添加相應的域名記錄,使得查詢到的結果相似以下。
$ dig +noall +answer SRV _etcd-server._tcp.example.com _etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra0.example.com. _etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra1.example.com. _etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra2.example.com.
(2) 分別爲各個域名配置相關的A記錄指向etcd核心節點對應的機器IP。使得查詢結果相似以下。
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com infra0.example.com. 300 IN A 10.0.1.10 infra1.example.com. 300 IN A 10.0.1.11 infra2.example.com. 300 IN A 10.0.1.12
(3) 作好了上述兩步DNS的配置,就可使用DNS啓動etcd集羣了。配置DNS解析的url參數爲-discovery-srv
,其中某一個節點地啓動命令以下。
$ etcd -name infra0 \ -discovery-srv example.com \ -initial-advertise-peer-urls http://infra0.example.com:2380 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster-state new \ -advertise-client-urls http://infra0.example.com:2379 \ -listen-client-urls http://infra0.example.com:2379 \ -listen-peer-urls http://infra0.example.com:2380
固然,你也能夠直接把節點的域名改爲IP來啓動。
etcd的啓動是從主目錄下的main.go
開始的,而後進入etcdmain/etcd.go
,載入配置參數。
若是被配置爲Proxy模式,則進入startProxy函數,不然進入startEtcd,開啓etcd服務模塊和http請求處理模塊。
在啓動http監聽時,爲了保持與集羣其餘etcd機器(peers)保持鏈接,都採用的transport.NewTimeoutListener
啓動方式,這樣在超過指定時間沒有得到響應時就會出現超時錯誤。
而在監聽client請求時,採用的是transport.NewKeepAliveListener
,有助於鏈接的穩定。
在etcdmain/etcd.go
中的setupCluster函數能夠看到,根據不一樣etcd的參數,啓動集羣的方法略有不一樣,可是最終須要的就是一個IP與端口構成的字符串。
在靜態配置的啓動方式中,集羣的全部信息都已經在給出,因此直接解析用逗號隔開的集羣url信息就行了。
DNS發現的方式相似,會預先發送一個tcp的SRV請求,先查看etcd-server-ssl._tcp.example.com
下是否有集羣的域名信息,若是沒有找到,則去查看etcd-server._tcp.example.com
。
根據找到的域名,解析出對應的IP和端口,即集羣的url信息。
較爲複雜是etcd式的自發現啓動。
首先就用自身單個的url構成一個集羣,而後在啓動的過程當中根據參數進入discovery/discovery.go
源碼的JoinCluster
函數。
由於咱們事先是知道啓動時使用的etcd的token地址的,裏面包含了集羣大小(size)信息。
在這個過程實際上是個不斷監測與等待的過程。
啓動的第一步就是在這個etcd的token目錄下注冊自身的信息,而後再監測token目錄下全部節點的數量,若是數量沒有達標,則循環等待。
當數量達到要求時,才結束,進入正常的啓動過程。
配置etcd過程當中一般要用到兩種url地址容易混淆
一種用於etcd集羣同步信息並保持鏈接,一般稱爲peer-urls;
另一種用於接收用戶端發來的HTTP請求,一般稱爲client-urls。
peer-urls
:一般監聽的端口爲2380
(老版本使用的端口爲7001
),包括全部已經在集羣中正常工做的全部節點的地址。client-urls
:一般監聽的端口爲2379
(老版本使用的端口爲4001
),爲適應複雜的網絡環境,新版etcd監聽客戶端請求的url從原來的1個變爲如今可配置的多個。這樣etcd能夠配合多塊網卡同時監聽不一樣網絡下的請求。
etcd集羣啓動完畢後,能夠在運行的過程當中對集羣進行重構,包括核心節點的增長、刪除、遷移、替換等。
運行時重構使得etcd集羣無須重啓便可改變集羣的配置,這也是新版etcd區別於舊版包含的新特性。
只有當集羣中多數節點正常的狀況下,你才能夠進行運行時的配置管理。
由於配置更改的信息也會被etcd當成一個信息存儲和同步,若是集羣多數節點損壞,集羣就失去了寫入數據的能力。
因此在配置etcd集羣數量時,強烈推薦至少配置3個核心節點。
當你節點所在的機器出現硬件故障,或者節點出現如數據目錄損壞等問題,致使節點永久性的不可恢復時,就須要對節點進行遷移或者替換。
當一個節點失效之後,必須儘快修復,由於etcd集羣正常運行的必要條件是集羣中多數節點都正常工做。
遷移一個節點須要進行四步操做:
增長節點可讓etcd的高可用性更強。
舉例來講,若是你有3個節點,那麼最多容許1個節點失效;當你有5個節點時,就能夠容許有2個節點失效。
同時,增長節點還可讓etcd集羣具備更好的讀性能。
由於etcd的節點都是實時同步的,每一個節點上都存儲了全部的信息,因此增長節點能夠從總體上提高讀的吞吐量。
增長一個節點須要進行兩步操做:
有時你不得不在提升etcd的寫性能和增長集羣高可用性上進行權衡。
Leader節點在提交一個寫記錄時,會把這個消息同步到每一個節點上,當獲得多數節點的贊成反饋後,纔會真正寫入數據。
因此節點越多,寫入性能越差。
在節點過多時,你可能須要移除一個或多個。
移除節點很是簡單,只須要一步操做,就是把集羣中這個節點的記錄刪除。而後對應機器上的該節點就會自動中止。
當集羣超過半數的節點都失效時,就須要經過手動的方式,強制性讓某個節點以本身爲Leader,利用原有數據啓動一個新集羣。
此時你須要進行兩步操做。
-force-new-cluster
加備份的數據從新啓動節點注意:強制性重啓是一個無可奈何的選擇,它會破壞一致性協議保證的安全性(若是操做時集羣中尚有其它節點在正常工做,就會出錯),因此在操做前請務必要保存好數據。
Proxy模式也是新版etcd的一個重要變動,etcd做爲一個反向代理把客戶的請求轉發給可用的etcd集羣。
這樣,你就能夠在每一臺機器都部署一個Proxy模式的etcd做爲本地服務,若是這些etcd Proxy都能正常運行,那麼你的服務發現必然是穩定可靠的。
因此Proxy並非直接加入到符合強一致性的etcd集羣中,也一樣的,Proxy並無增長集羣的可靠性,固然也沒有下降集羣的寫入性能。
那麼,爲何要有Proxy模式而不是直接增長etcd核心節點呢?
實際上etcd每增長一個核心節點(peer),都會增長Leader節點必定程度的包括網絡、CPU和磁盤的負擔,由於每次信息的變化都須要進行同步備份。
增長etcd的核心節點可讓整個集羣具備更高的可靠性,可是當數量達到必定程度之後,增長可靠性帶來的好處就變得不那麼明顯,反卻是下降了集羣寫入同步的性能。
所以,增長一個輕量級的Proxy模式etcd節點是對直接增長etcd核心節點的一個有效代替。
熟悉0.4.6這個舊版本etcd的用戶會發現,Proxy模式其實是取代了原先的Standby模式。
Standby模式除了轉發代理的功能之外,還會在覈心節點由於故障致使數量不足的時候,從Standby模式轉爲正常節點模式。
而當那個故障的節點恢復時,發現etcd的核心節點數量已經達到的預先設置的值,就會轉爲Standby模式。
可是新版etcd中,只會在最初啓動etcd集羣時,發現核心節點的數量已經知足要求時,自動啓用Proxy模式,反之則並未實現。主要緣由以下。
基於上述緣由,目前Proxy模式有轉發代理功能,而不會進行角色轉換。
從代碼中能夠看到,Proxy模式的本質就是起一個HTTP代理服務器,把客戶發到這個服務器的請求轉發給別的etcd節點。
etcd目前支持讀寫皆可和只讀兩種模式。
默認狀況下是讀寫皆可,就是把讀、寫兩種請求都進行轉發。
只讀模式只轉發讀的請求,對全部其餘請求返回501錯誤。
值得注意的是,除了啓動過程當中由於設置了proxy
參數會做爲Proxy模式啓動。
在etcd集羣化啓動時,節點註冊自身的時候監測到集羣的實際節點數量已經符合要求,那麼就會退化爲Proxy模式。
etcd的存儲分爲內存存儲和持久化(硬盤)存儲兩部分
內存中的存儲除了順序化的記錄下全部用戶對節點數據變動的記錄外,還會對用戶數據進行索引、建堆等方便查詢的操做。
持久化則使用預寫式日誌(WAL:Write Ahead Log)進行記錄存儲。
在WAL的體系中,全部的數據在提交以前都會進行日誌記錄。
在etcd的持久化存儲目錄中,有兩個子目錄。
一個是WAL,存儲着全部事務的變化記錄;
另外一個則是snapshot,用於存儲某一個時刻etcd全部目錄的數據。
經過WAL和snapshot相結合的方式,etcd能夠有效的進行數據存儲和節點故障恢復等操做。
既然有了WAL實時存儲了全部的變動,爲何還須要snapshot呢?
隨着使用量的增長,WAL存儲的數據會暴增,爲了防止磁盤很快就爆滿,etcd默認每10000條記錄作一次snapshot,通過snapshot之後的WAL文件就能夠刪除。
而經過API能夠查詢的歷史etcd操做默認爲1000條。
首次啓動時,etcd會把啓動的配置信息存儲到data-dir
參數指定的數據目錄中。
配置信息包括本地節點的ID、集羣ID和初始時集羣信息。
用戶須要避免etcd從一個過時的數據目錄中從新啓動,由於使用過時的數據目錄啓動的節點會與集羣中的其餘節點產生不一致(如:以前已經記錄並贊成Leader節點存儲某個信息,重啓後又向Leader節點申請這個信息)。
因此,爲了最大化集羣的安全性,一旦有任何數據損壞或丟失的可能性,你就應該把這個節點從集羣中移除,而後加入一個不帶數據目錄的新節點。
WAL(Write Ahead Log)最大的做用是記錄了整個數據變化的所有歷程。
在etcd中,全部數據的修改在提交前,都要先寫入到WAL中。
使用WAL進行數據的存儲使得etcd擁有兩個重要功能。
在etcd的數據目錄中,WAL文件以$seq-$index.wal
的格式存儲。
最初始的WAL文件是0000000000000000-0000000000000000.wal
,表示是全部WAL文件中的第0個,初始的Raft狀態編號爲0。
運行一段時間後可能須要進行日誌切分,把新的條目放到一個新的WAL文件中。
假設,當集羣運行到Raft狀態爲20時,須要進行WAL文件的切分時,下一份WAL文件就會變爲0000000000000001-0000000000000021.wal
。
若是在10次操做後又進行了一第二天志切分,那麼後一次的WAL文件名會變爲0000000000000002-0000000000000031.wal
。
能夠看到-
符號前面的數字是每次切分後自增1,而-
符號後面的數字則是根據實際存儲的Raft起始狀態來定。
snapshot的存儲命名則比較容易理解,以$term-$index.wal
格式進行命名存儲。
term和index就表示存儲snapshot時數據所在的raft節點狀態,當前的任期編號以及數據項位置信息。
從代碼邏輯中能夠看到,WAL有兩種模式,讀模式(read)和數據添加(append)模式,兩種模式不能同時成立。
一個新建立的WAL文件處於append模式,而且不會進入到read模式。
一個原本存在的WAL文件被打開的時候必然是read模式,而且只有在全部記錄都被讀完的時候,才能進入append模式,進入append模式後也不會再進入read模式。這樣作有助於保證數據的完整與準確。
集羣在進入到etcdserver/server.go
的NewServer
函數準備啓動一個etcd節點時,會檢測是否存在之前的遺留WAL數據。
檢測的第一步是查看snapshot文件夾下是否有符合規範的文件,若檢測到snapshot格式是v0.4的,則調用函數升級到v0.5。
從snapshot中得到集羣的配置信息,包括token、其餘節點的信息等等
而後載入WAL目錄的內容,從小到大進行排序。根據snapshot中獲得的term和index,找到WAL緊接着snapshot下一條的記錄,而後向後更新,直到全部WAL包的entry都已經遍歷完畢,Entry記錄到ents變量中存儲在內存裏。
此時WAL就進入append模式,爲數據項添加進行準備。
當WAL文件中數據項內容過大達到設定值(默認爲10000)時,會進行WAL的切分,同時進行snapshot操做。
這個過程能夠在etcdserver/server.go
的snapshot
函數中看到。
因此,實際上數據目錄中有用的snapshot和WAL文件各只有一個,默認狀況下etcd會各保留5個歷史文件。
新版etcd中,raft包就是對Raft一致性算法的具體實現。
關於Raft算法的講解,網上已經有不少文章,有興趣的讀者能夠去閱讀一下Raft算法論文很是精彩。
本文則再也不對Raft算法進行詳細描述,而是結合etcd,針對算法中一些關鍵內容以問答的形式進行講解。
有關Raft算法的術語若是不理解,能夠參見概念詞彙表一節。
在etcd代碼中,Node做爲Raft狀態機的具體實現,是整個算法的關鍵,也是瞭解算法的入口。
在etcd中,對Raft算法的調用以下,你能夠在etcdserver/raft.go
中的startNode
找到:
storage := raft.NewMemoryStorage() n := raft.StartNode(0x01, []int64{0x02, 0x03}, 3, 1, storage)
經過這段代碼能夠了解到,Raft在運行過程記錄數據和狀態都是保存在內存中,而代碼中raft.StartNode
啓動的Node就是Raft狀態機Node。
啓動了一個Node節點後,Raft會作以下事項。
1. 首先,你須要把從集羣的其餘機器上收到的信息推送到Node節點,你能夠在etcdserver/server.go
中的Process
函數看到。
func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error { if m.Type == raftpb.MsgApp { s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size()) } return s.node.Step(ctx, m) }
在檢測發來請求的機器是不是集羣中的節點,自身節點是不是Follower,把發來請求的機器做爲Leader,具體對Node節點信息的推送和處理則經過node.Step()
函數實現。
2. 其次,你須要把日誌項存儲起來,在你的應用中執行提交的日誌項,而後把完成信號發送給集羣中的其它節點,再經過node.Ready()
監聽等待下一次任務執行。
有一點很是重要,你必須確保在你發送完成消息給其餘節點以前,你的日誌項內容已經確切穩定的存儲下來了。
3. 最後,你須要保持一個心跳信號Tick()
。
Raft有兩個很重要的地方用到超時機制:心跳保持和Leader競選。
須要用戶在其raft的Node節點上週期性的調用Tick()函數,以便爲超時機制服務。
綜上所述,整個raft節點的狀態機循環相似以下所示:
for { select { case <-s.Ticker: n.Tick() case rd := <-s.Node.Ready(): saveToStorage(rd.State, rd.Entries) send(rd.Messages) process(rd.CommittedEntries) s.Node.Advance() case <-s.done: return } }
而這個狀態機真實存在的代碼位置爲etcdserver/server.go
中的run
函數。
對狀態機進行狀態變動(如用戶數據更新等)則是調用n.Propose(ctx, data)
函數,在存儲數據時,會先進行序列化操做。
得到大多數其餘節點的確認後,數據會被提交,存爲已提交狀態。
以前提到etcd集羣的啓動須要藉助別的etcd集羣或者DNS,而啓動完畢後這些外力
就不須要了,etcd會把自身集羣的信息做爲狀態存儲起來。
因此要變動自身集羣節點數量實際上也須要像用戶數據變動那樣添加數據條目到Raft狀態機中。這一切由n.ProposeConfChange(ctx, cc)
實現。
當集羣配置信息變動的請求一樣獲得大多數節點的確認反饋後,再進行配置變動的正式操做,代碼以下。
var cc raftpb.ConfChange cc.Unmarshal(data) n.ApplyConfChange(cc)
注意:一個ID惟一性的表示了一個集羣,因此爲了不不一樣etcd集羣消息混亂,ID須要確保惟一性,不能重複使用舊的token數據做爲ID。
Store這個模塊顧名思義,就像一個商店把etcd已經準備好的各項底層支持加工起來,爲用戶提供五花八門的API支持,處理用戶的各項請求。
要理解Store,只須要從etcd的API入手便可。打開etcd的API列表,咱們能夠看到有以下API是對etcd存儲的鍵值進行的操做,亦即Store提供的內容。
API中提到的目錄(Directory)和鍵(Key),上文中也可能稱爲etcd節點(Node)。
curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello world" { "action": "set", "node": { "createdIndex": 2, "key": "/message", "modifiedIndex": 2, "value": "Hello world" } }
反饋的內容含義以下:
curl http://127.0.0.1:2379/v2/keys/message
prevNode
值反應了修改前存儲的內容。curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello etcd"
curl http://127.0.0.1:2379/v2/keys/message -XDELETE
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl= -d prevExist=true
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=7'
POST
參數,會自動在該目錄下建立一個以createdIndex值爲鍵的值,這樣就至關於以建立時間前後嚴格排序了。這個API對分佈式隊列這類場景很是有用。curl http://127.0.0.1:2379/v2/keys/queue -XPOST -d value=Job1 { "action": "create", "node": { "createdIndex": 6, "key": "/queue/6", "modifiedIndex": 6, "value": "Job1" } }
curl -s 'http://127.0.0.1:2379/v2/keys/queue?recursive=true&sorted=true'
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true -d prevExist=true
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=one
curl http://127.0.0.1:2379/v2/keys/foo?prevExist=false -XPUT -d value=three
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d dir=true
curl http://127.0.0.1:2379/v2/keys/
/
結尾。還能夠經過recursive參數遞歸列出全部子目錄信息。curl http://127.0.0.1:2379/v2/keys/
recursive=true
參數。curl 'http://127.0.0.1:2379/v2/keys/foo_dir?dir=true' -XDELETE
_
開頭默認就是隱藏鍵。curl http://127.0.0.1:2379/v2/keys/_message -XPUT -d value="Hello hidden world"
相信看完這麼多API,讀者已經對Store的工做內容基本瞭解了。
它對etcd下存儲的數據進行加工,建立出如文件系統般的樹狀結構供用戶快速查詢。
它有一個Watcher
用於節點變動的實時反饋,還須要維護一個WatcherHub
對全部Watcher
訂閱者進行通知的推送。
同時,它還維護了一個由定時鍵構成的小頂堆,快速返回下一個要超時的鍵。
最後,全部這些API的請求都以事件的形式存儲在事件隊列中等待處理。
經過從應用場景到源碼分析的一系列回顧,咱們瞭解到etcd並非一個簡單的分佈式鍵值存儲系統。
它解決了分佈式場景中最爲常見的一致性問題,爲服務發現提供了一個穩定高可用的消息註冊倉庫,爲以微服務協同工做的架構提供了無限的可能。
相信在不久的未來,經過etcd構建起來的大型系統會愈來愈多。
孫健波,浙江大學SEL實驗室碩士研究生,目前在雲平臺團隊從事科研和開發工做。
浙大團隊對PaaS、Docker、大數據和主流開源雲計算技術有深刻的研究和二次開發經驗,團隊現將部分技術文章貢獻出來,但願能對讀者有所幫助。