系列文章目錄
淺談分布式系統與一緻性協定(一)
淺談分布式系統與一緻性協定(二)
淺談分布式系統與一緻性協定(三)
深入淺出之etcd
深入淺出之etcd(二)
etcd版本之v3
etcd之安全性闡述
etcd的多版本并發控制
概述
etcd v3存儲的資料通過KV API對外暴露,并在API的層級支援mini事務。并且為了保證向後相容,保留了etcd v2的協定與API。也就是說etcd v2和etcd v3本質上是共享一套Raft協定代碼的,差別是API不同,存儲不同,資料互相隔離。v2的資料隻能通過v2的API通路,v3的資料隻能通過v3的API通路。
etcd v2 到 etcd v3
etcd廣泛應用到分布式網絡,服務發現,配置中心,分布式系統排程和負載均衡領域。etcd專注于key-value存儲而不是完整的資料庫,通過HTTP+JSON的方式暴露給外部API,watch機制提供持續監聽某個key變化的功能,基于TTL的key自動過期機制,這些特性很好的滿足了etcd的初步需求。但是,也出現了一些問題,用戶端需要頻繁的與服務端進行通信,叢集即使在空閑時間也需要承受很大的壓力,垃圾回收key的時間不穩定。是以,etcd v3借鑒了etcd v2的經驗,做出了如下優化:
- 使用gRPC+protobuf取代HTTP+JSON通信,提高通信效率,另外,通過gRPC gateway來繼續保持對HTTP JSON接口的支援
- 使用lease(租約)的key自動過期機制,取代了TTL的key自動過期機制 watcher機制進行優化,基于HTTP/2的server push,并且對時間進行了多路複用優化
- etcd v3資料模型改變,v2版本的etcd是一個簡單的kv記憶體資料庫,而etcd v3是一個支援事務和多版本并發控制的磁盤資料庫。etcd v2資料不直接落盤,落盤的日志和快照檔案隻是資料的中間格式而非最終形式,系統通過回放日志檔案來建構資料的最終形式。etcd v3落盤是資料的最終形式,日志和快照檔案的主要作用是分布式的複制
gRPC
gRPC是一個高性能,跨語言的RPC架構,基于HTTP/2協定實作。使用protobuf作為序列化和反序列化協定,即基于protobuf來聲明資料模型和RPC接口服務
序列化和反序列化優化
protobuf效率高,遠高于JSON,etcd v3的gRPC序列化和反序列化的速度是etcd v2的兩倍多
減少了TCP連接配接
etcd v2的通信協定使用HTTP/1.1,gRPC支援HTTP/2,HTTP/2對HTTP通信進行多路複用,可以共享一個TCP連接配接。是以etcd v3大大減少了用戶端與服務端的連接配接數,一個用戶端與服務端建立一個TCP連接配接,而etcd v2,一個用戶端需要與服務端建立多個連接配接,每個HTTP請求都需要建立連接配接
租約機制
etcd v2的key自動過期是基于TTL的,用戶端可以為一個key設定自動過期時間,一旦TTL到了。服務端就自動删除該key。如果用戶端不想服務端删除某個key,就需要定期更新這個key的TTL。也就是說即使整個叢集都處于空閑狀态,也會也很多用戶端需要與伺服器進行定期通信,以保證某個key不會自動被删除。而且TTL都是設定在key上,那麼對于用戶端想保留每個key,用戶端需要對每個key進行定期更新,即使這些key的過期時間都是一樣的
etcd v3使用(lease)租約機制,替代了基于TTL的自動過期機制。使用者建立一個lease,然後将這個租約與key關聯起來。一旦租約過期,etcd v3服務端就會删除與這個租約關聯的所有的key。即,多個key的過期時間是一樣的,這些key可以共享一個租約。這樣減少了用戶端請求的數量,對于共享一個租約的key,用戶端隻需要更新這個租約的過期時間即可,不需要更新所有的key
etcd v3 的watch機制
watch機制使得用戶端可以監控一個key的變化,當key發生變化時,服務端将通知用戶端,而不是讓用戶端定期向伺服器發送請求去輪詢key的變化。etcd v2的服務端對每個用戶端的每個watch請求都維持着一個HTTP長連接配接,如果數千個用戶端watch數千個key,那麼etcd v2服務端的socket和記憶體資源會很快消耗掉。
etcd v3對同一個用戶端的watch請求進行多路複用,這樣的化,同一個用戶端隻需要與服務端維護一個TCP連接配接即可,這樣大大減少了服務端壓力
etcd v2的每個watcher都會占用一個TCP資源和一個goroutine資源,大概消耗30~40kb。etcd v3減少每個Watcher帶來的資源消耗以此支援了大規模的watch,同樣的使用者不同的watcher隻消耗一個go routine,這樣再一次減輕了伺服器資源的消耗
etcd v3的資料存儲模型
etcd 時一個key-value資料庫,etcd v2隻儲存key的最新的value,之前的value直接覆寫掉了。但是etcd v2維護了一個全局的key例是記錄變更視窗,預設儲存最新的1000個變更,。etcd v2隻儲存1000個曆史變更,這樣帶來的後果是watch丢失事件。etcd v3抛棄了這種設計,引入了MVCC(多版本并發控制),采用了曆史紀錄為主索引的存儲結構,儲存了key的所有變更記錄。etcd v3可以存儲十萬個曆史記錄進行快速查詢,并且更具使用者要求進行壓縮合并。由于etcd v3實作了MVCC,記錄了所有的曆史記錄,是以這些資料不能都放在記憶體中。是以,etcd v3是一個磁盤資料庫,底層的存儲引擎使用BoltDB
etcd v3的迷你事務
很多情況下,用戶端需要同時去讀或者寫一個key,或者很多個key。提供同步原語防止資料競争是重要的。etcd v3中引入了迷你事務(mini-transaction)的概念。每個迷你事務都可以包含一些列條件語句,隻有在滿足特定條件下事務才會執行。迷你事務支援原子的比較多個鍵值并且操作多個鍵值。之前CAS是特殊的針對單個key的迷你事務。
gRPC服務
發送至etcd v3伺服器的每一個API請求均為gRPC遠端過程調用。etcd v3将其歸類為不同的服務(service),而service又可分為方法(method)定義和消息(message)定義。
根據etcd v3的所定義的不同服務,其API可以分為鍵值(KV),叢集(Cluster),維護(Maintenance),認證/鑒權(Auth),觀察(Watch)與租約(Lease)六大類
各類服務所包含的方法具體描述了與其對應的API所具備的功能,從大的角度概括,這些服務又可以分為兩類,其中一類是管理叢集的API,具體包括如下功能:
-
Auth
Service可能是某項鑒定過程以及處理鑒定的請求,比如,增加或者删除使用者,更改使用者密碼,查詢使用者資訊和擷取使用者清單,以及授權或者撤銷使用者角色,增加或者删除角色,查詢角色資訊和擷取角色清單,以及為角色授予或撤銷某項特定的key
- Cluster Service用于在叢集中增加或删除成員,更新成員配置,以及得到叢集中包含所有成員的清單
- Maintenance Service則提供了啟動或者停止警報以及查詢警報的功能,還可以查詢成員的狀态資訊,為成員後端資料庫整理碎片,在client的流中發送某成員的完整後端快照
另外一大類是處理etcd鍵值空間的API,具體包括如下:
- KV Service:用于建立,更新,擷取以及删除鍵值對
- Watch Service:用于監聽key的變化
- Lease Service:用于消耗用戶端keep-alive消息的原語
請求和響應
etcd v3的所有RPC都遵循相同得格式。每個RPC都形如一個函數聲明,都有一個入參和傳回值。
etcd v3 API的所有響應都攜帶一個響應頭部,包含了etcd叢集的中繼資料。示例代碼如下:
message ResponseHeader {
uint64ccluster_id = 1 ;
uint64 member_id = 2 ;
int64 revision = 3;
uint64 raft_term = 4;
}
上述代碼字段說明如下:
- cluster_id:生成響應的cluster ID
- member_id:生成該相應的member ID
- revision:生成該響應的鍵值存儲的版本。修改etcd背景鍵值存儲的每一步操作都會被賦予一個單調遞增的版本号(revision)。一個事物可能修改多次背景鍵值存儲,但隻會産生一個revision。被操作修改的鍵值對的revision屬性與操作的revision具有相同的值。revision可以用作背景鍵值存儲的邏輯鎖。擁有更大revision值的鍵值對肯定是revision值較小的鍵值對之後被修改,兩個revision相同的鍵值對肯定被某個操作(一般是事務)同時修改
-
Raft_Term:生成該響應的member所處的Raft協定任期(term)
用戶端可以通過檢查Cluster_ID或者Member_ID子段的值來确認是否正在與目标叢集或節點通信
用戶端可以通過revision的值擷取發生該操作時,etcd叢集後端鍵值存儲最新revision。用戶端可以通過Rafr_Term的值來檢測etcd叢集是否完成了一次新的上司人選舉
KV API
鍵值對(key-value pair)是KV API所能處理的最小機關,每個鍵值對均包含一些protobuf格式的子段
message KetValue {
bytes key = 1;
bytes value = 2;
int64 create_revision = 3;
int64 mod_revision = 4;
int64 version = 5 ;
int64 lease = ;
}
從上面定義可以看出,在KV message中,除了key-value映射值及其lease資訊之外,還有一類重要的revsiosn中繼資料(包括create_revision和mod_revsion)。這些revsion資訊可以根據建立時間和修改時間對key進行排序
revision
etcd 的revision本質上就是etcd維護的一個在叢集範圍有效的64位計數器。隻要etcd的鍵空間發生變化,ervision值也會行吟閣增加。也可以jiangrevision看成是全局的邏輯時鐘,即所有針對後端存儲的修改操作進行連續的排序。revision的值是單調遞增的,而與某個revision相關聯的資料則是那些改變了後端存儲的資料。從内部實作來看,出現一個revision,就意味着某些修改寫入了後端的B+樹,而這些修改柴采用增大的revision作為索引
對于etcd v3的多版本并發控制,revision價值不言而喻。MVCC模型是指由于儲存了鍵空間的曆史,是以可以檢視過去某個revision(版本)的key-value的存儲。同時為了實作細粒度的存儲管理,叢集管理者可以自定義配置鍵空間曆史儲存政策。etcd v3會借助自定義的計時器廢棄舊鍵的版本(revision)典型的etcd v3叢集可以使被替代的key的資料保留數小時。是以,etcd v3具備對用戶端長時間斷開連接配接的可靠處理能力,突破了僅能處理暫态網絡中斷的限制。在這種情況下,watch用戶端可以直接根據最近以此觀察到的revision進行恢複。類似的,如果用戶端希望讀取某個時間點的key-value存儲狀态,則隻需要在請求中附帶某個revision的值即可傳回該revision送出時間點的key空間狀态
鍵區間
etcd v3資料模型采用扁平key空間,為所有key建立索引。該模型有别于其他常見的采用層級系統将key組建為目錄(directory)的key-value存儲系統。key不再以目錄的形式列出,而是左閉右開的形式,如[ key1,keyN)。對于key去就按的操作,既保留了對目錄形式key的查找能力。
事務
在 etcd v3中,事務就是一個原子的,針對key-value存儲操作的If/Then/Else結構。事務提供了一個原語,用于将請求歸并到一起放在原子塊中,這些原子塊的執行條件以key-value存儲裡的内容為依據。事務可以用來保護key不受其他并發更新操作的修改,也可以建構CAS操作,并以此作為更高層次并發控制的基礎。
所有的事務都由一個比較“連接配接(conjunction)”來守護,類似于if聲明,每個比較都會檢查背景存儲中的一個key。這個檢查可以是如下内容:該key在背景存儲是否有value?該key的value是否等于某個給定的值?除了value,還可以檢查這個key的revision或者version。。如果所有的比較都傳回true,那麼就說明該事務成功執行了,并且執行該事務success請求塊中的操作,反之就說明該事務失敗了,轉而執行該事務failure請求塊中的操作
Event
對于每個key而言,發生的每一個變化都以Event消息進行表示。一個Event消息提供了變化的類型與對應改變的資料,其字段定義代碼如下:
message Event{
enum EventType{
PUT = 0;
DELETE = 1;
}
EventType type =1;
KeyValue kv = 2; //與Event管來奶的key-value
KeyValue prev_kv = 3; //對應緊接着Event之前的revision的key-value
}
-
type
:Event類型,分為PUT類型和DELETE類型。PUT類型表明新的資料已經存儲到對應的key,DELETE類型表明key已經被删除了
- KV:與Event關聯的key-value。一個PUT Event包含目前的KV,一個Version=1的PUT Event表明這個key是建立的。一個DELETE Event包含被删除的key和該key被删除時的modification revision
- Prev——KV:該key在發生此Event之前最近一刻revision的key-value對。為了節省帶寬,隻有在Watch請求中顯示地啟動該選項時才會在響應中傳回該值
流式Watch
wacth操作是長期持續存在地請求,watch流是雙向的。Client通過寫入流來建立watch,另一方面,Client通過讀入流來接收watch到的Event。
etcd v3的watch機制確定檢測到的Event具有有序,可靠與原子化的特點,各個特點對應的意義如下:
- 有序:Event按照revision排序,後發生的Event不會在前面的Event之前出現在watch流中
- 可靠:某個事件序列不會一樓1其中任意的子序列,假設有三個Event,按發生事件依次排序a<b<b,如果watch接收到Event a和c,那麼能保證b也被接收了
- 原子性:Event清單確定完整的revision,在相同得revision多個key上,更新不會分裂為幾個事件清單
Lease API
租約是一種檢測用戶端活躍度的機制。Lease機制應用廣泛。租約是有生存時間的,叢集為租約授予一個TTL。當key被授予一個租約時,他的生存時間即為Lease生存時間。當Lease的TTL到期時,所有與之關聯的key都會被删除。
如果etcd叢集在給定的TT周期内沒有收到一個keepAlive消息維持租約,那麼該租約将過期。etcd3所支援的租約機制可為etcd中的某一個或者多個key關聯租約,一個key最多關聯一個租約。當一個租約過期時,所有關聯的key都會被自動删除。每個過期的key都會産生一個“删除”事件