共識演算法
希望所有replica servers能夠達成Strong Consistency
一致性的定義 :
每一個節點儲存相同且順序一致的資料,同時保證使用者的每次讀取操作皆返回最新的資訊。
「共識演算法」便是用來達成「一致性」的方法論。
它需要讓分散式集群中的每一個節點滿足以下條件
儲存相同的數據
並且對某一個提案(proposal)達成一致認同並保存
Termination: 保證所有參與決策且無故障的節點最終會做一個決定,執行的演算法不會無法終止,也稱為Liveness。
Agreement: 當演算法完成時,所有節點都會做相同的決定,不會再變動,也稱為Safety。
Validity: 最終的決議一定是來自某一個參與的節點。
No completely asynchronous consensus protocol can tolerate even a single unannounced process death. [Impossibility of Distributed Consensus with One Faulty Process, Journal of the Association for Computing Machinery, Vol. 32, No. 2, April 1985]
在Asynchronous網路環境且允許一個節點失效的情況下,任何Consensus protocol無法確保共識在有限時間內完成。
從理論上也許完美的共識演算法並不存在。但是實作上我們仍然能夠實作出一個演算法,並且降低沒有達成共識的機率到合乎使用
Leader:
負責與client互動
log replication
任一時刻,集群只會有一個leader
Follower
相較於leader完全被動
不會主動發送任何RPC,只會回應傳入的RPC
Candidate
暫時的角色,可以被選為新leader
Infinite State Machine
Term是一個嚴格遞增的數字,也是Raft中各個Servers的時間單位 改變term的方法有2種: 1. 收到其他Servers的Call後更新自己的Term 2. Election Timeout晉身為Candidate後遞增自己的term
不同的Server紀錄的term可能會不同(因為是asynchronous的可能有failure-server)
因此Term也是Raft Servers判斷過期資訊的重要方式
RequestVote RPC: 在選出新的leader時使用
AppendEntries RPC: 在normal operation時,leader發送此RPC來replicate log至其他server
Leader election
Normal operation
(basic log replication)
每一個server初始為 Follower ,相信著這個cluster有一個leader的存在,被動的接受Leader或是Candidate的RPC並回應。
若是Followers超過了election Timeout 仍然沒有收到任何RPC,則
認定系統目前沒有leader
follower轉成candidate發起選舉
自己紀錄的 term + 1
轉成 Candidate state
投給自己
發送 RequestVote RPCs 給其餘的servers,不斷嘗試直到
收到過半數的server的選票
變成 Leader
開始發送 AppendEntries heartbeats 給其他servers
收到合法Leader的RPC
退回 Follower
這一輪沒有人拿到過半數的票 (election Timeout超時)
current_term 再遞增一次
發起下一輪選舉,回到 步驟1
Node1成為Leader之後, 會定期被heartbeat timer觸發去對集群中其他節點發送心跳
刷新其他Follower節點的election timer, 避免超時而觸發新一輪的選舉
所以當Follower收到heartbeat後, 會重置election timer, 並且給出heartbeat response. 讓Leader知道Follower也還正常活著.
client 發送請求或是指令給 leader
leader 將該指令存到log中
leader 發送 AppendEntries RPCs 給 followers,follower儲存並回復收到。
當一個 log entry 被 commited :
Leader可以將該command送去state machine執行,並返回結果給client
Leader在下一次的AppendEntries RPCs通知followerd該log entry被commited
followers也可以把該command送去執行。
到這邊為止我們已經對Raft Algo.的運作有一個基本的認識與想像空間了 => 先來看一次動畫
但是為了滿足前面提到的Consensus Algo的Requirements,Raft必須要再定義清楚更細項的規則
所以我們接下來的矛頭就會指向這些細節的部分!
1. Safety-Guaranteed:如何選出 leader,並且保證任一時刻只有一個leader
2. Liveness-Guaranteed:當leader失效如何偵測,並且選出新leader
每一個server在同一個term裡只能投一張票 (這個重要資訊必須存在disk,否則投完票,失敗重啟可能會重複投票)
不可能有兩個candidates同時拿到過半數的選票
因此一定有一個candidate有機會比別人先timeout進入下一輪選舉,讓大家投給他
我們會先預設Election Timeout一個value
設為T
每個Follower節點的election timeout會落在[T, 2*T - 1]這範圍內的millisecond來倒數
Raft同時也需要確保Servers之間Log的一致性,它所選用的方法是由Leader => Follower的Log Replication(日誌複製)
每一個 AppendEntries RPCs 除了包含最新的entry外,還包含 前一個entry的(index, term)
Followers 一定要擁有同樣的前一個entry的(index, term) 才會儲存最新的log entry,否則就不接受該RPC
Leader保存每一個follower的nextIndex,表示下一個AppendEntries RPCs要送哪一個index的entry給該follower,如果發現AppendEntries失敗則遞減nextIndex
若Followers收到AppendEntries RPC時發現nextIndex指向的位置已經有entry了,會認定該entry不重要並以從leader收到的RPC內容進行覆寫
回到Raft最初的目的,我們希望的是集群中的每一個機器皆執行一樣且順序一致的指令。
當一個log entry被state machine執行時,其他的state machine執行同一個log entry時,一定也是執行同一個指令,這樣所有機器的狀態跟結果才會一樣。
如果一個log entry被目前的leader決定committed(過半數都保存該log entry),則
=> 該commited的log entry一定會存在未來的leader中
針對Commitment Safety,我們會需要修正兩個部分
Leader Election
防止選到擁有過期資訊的Leader
Commitment Rules
保持Commitent的嚴謹性
Candidate的 RequestVote RPCs 須包含自己 最後一個log entry的 (index,term)
收到 server U RequestVote RPCs的 server V,檢查自己最後一個log entry的term與index是否比接收到的RPC還要新,是的話則拒絕投給該Candidates,因為自己的log更多更完整。
上述保證leader一定擁有最完整的log entries
Leader要確保Append給Followers的Log entry收到過半數來自Followers的回應才可以執行commit並將commit的status append給Followers
假設發生 Network Partition 使得舊的leader聯絡不上followers,導致剩餘的followers發起選舉選出新的leader 此時,舊的leader回復連線,打算commit log entries?
對於每一個RPC都包含目前發送者的最新Term
如果接收者發現發送者的Term更舊,就拒絕該RPC請求,回傳最新的Term
發送者收到回覆退回follower,並更新成新的Term
如果是接收者的Term更舊,接收者退回follower,並更新成新的Term
這裡再提供另外一個Raft的介紹動畫
http://thesecretlivesofdata.com/raft/
他是以故事的方式去介紹他,我覺得做的蠻不錯的
給推!
Etcd是一個開源的分布式鍵值存儲(key-value),它由CoreOS團隊開發,現在由Cloud Native Computing Foundation負責管理
它適用於各種作業系統,包括Linux、BSD和OS X。
相信大家對這個logo並不陌生
Kubernetes底層就是依賴於etcd實現一切與cluster狀態跟配置的相關管理
1. 健康檢查:服務節點定期向etcd發送心跳更新自己訊息的TTL
2. 服務主動註冊:同一類型的服務啟動後, 主動註冊到相同服務目錄下
3. 方便透過服務名稱就能查詢到服務提供給外部訪問的IP與Port
4. 服務之間能彼此即時感知, 新增節點、丟棄不可用的服務節點.
其實就是在描述在分散式系統中的重要功能:服務註冊與發現
5. 服務之間共享同一份配置文件, 並且監聽著, 有更新, 服務立刻感知
6. 可靠的分散式KV儲存, 底層用Raft保證一致性
7. etcdV3套件使用gRPC跟etcd服務進行溝通; 相較于v2版本的http+json更加輕巧
8. 能夠容忍單點故障(Fault Tolerance), 能夠應付網路分區
分散式鎖
分散任務進度匯報
Distributed Queue
BoltDB也是用Go寫的,一個嵌入式ACID key/value pair DB
支援One-Writer / Multi-Readers的MVCC
Isolation level為Serializable isolation
內部資料以B+Tree做儲存
etcd把原始的key當成Btree的索引存放在記憶體中, 資料節點放的是"keyIndex"的資料節點。
所以每次etcd啟動時, 都會去BoltDB把所有的資料撈出來放在記憶體中。
type keyIndex struct {
key []byte // 當前用戶操作的key
modified revision // 紀錄最新一次修改對應的revision
generations []generation //過往修改歷程
}
type generation struct {
ver int64 // 從0開始, 0表示第一個版本
created revision // 表示這generation被建立的時間
revs []revision // 表示該世代所有版本
}
type revision struct {
// 目前的事務操作ID(timestamp), 每次事務操作時, 遞增1
main int64
// 子ID, 從0開始; 同一個事務內的操作遞增1
sub int64
}
因為MVCC(多版本控管),不可能讓所有版本都追到同一個revs中
除了不會讓revs長度無限膨脹之外,也因為長度有限便於快速搜尋。
KV : 負責KV操作, 增刪K-V, 修改Value, 觸發compact
Watch : 負責對指定的Key增加Watch(觀察者模式)
Lease : 負責租約的操作, 新建和回收租約, 續約.
Cluster : 集群節點的管理操作, 增刪集群的節點, 或是查詢節點的狀態
Maintenance : 提供了Alarm功能, 獲取節點的狀態, 進行碎片整理, 取得快照, 轉移Leader
Auth : 負責權限相關的操作, 添加用戶or角色,為用戶分配角色, 啟/停用身份驗證.
Main Focusing