Raft 適用於一個管理日誌一致性的協議,相比於 Paxos 協議 Raft 更易於理解和去實現它。
爲了提升理解性,Raft 將一致性算法分爲了幾個部分,包括領導選取(leader selection)、日誌複製(log replication)、安全(safety),而且使用了更強的一致性來減小了必須須要考慮的狀態。html
相比Paxos,Raft算法理解起來更加直觀。git
Raft算法將Server劃分爲3種狀態,或者也能夠稱做角色:github
Leader
負責Client交互和log複製,同一時刻系統中最多存在1個。算法
Follower
被動響應請求RPC,從不主動發起請求RPC。安全
Candidate服務器
一種臨時的角色,只存在於leader的選舉階段,某個節點想要變成leader,那麼就發起投票請求,同時本身變成candidate。若是選舉成功,則變爲candidate,不然退回爲follower架構
狀態或者說角色的流轉以下:併發
在Raft中,問題分解爲:領導選取、日誌複製、安全和成員變化。分佈式
複製狀態機經過複製日誌來實現:ide
日誌:每臺機器保存一份日誌,日誌來自於客戶端的請求,包含一系列的命令
狀態機:狀態機會按順序執行這些命令
一致性模型:分佈式環境下,保證多機的日誌是一致的,這樣回放到狀態機中的狀態是一致的
Raft中有Term的概念,Term類比中國歷史上的朝代更替,Raft 算法將時間劃分紅爲任意不一樣長度的任期(term)。
一、follower增長當前的term,轉變爲candidate。
二、candidate投票給本身,併發送RequestVote RPC給集羣中的其餘服務器。
三、收到RequestVote的服務器,在同一term中只會按照先到先得投票給至多一個candidate。且只會投票給log至少和自身同樣新的candidate。
關於Raft更詳細的描述,能夠查看這裏,從分佈式一致性到共識機制(二)Raft算法
Spring Cloud Alibaba Nacos 在 1.0.0 正式支持 AP 和 CP 兩種一致性協議,其中的CP一致性協議實現,是基於簡化的 Raft 的 CP 一致性。
Nacos server在啓動時,會經過RunningConfig.onApplicationEvent()方法調用RaftCore.init()方法。
public static void init() throws Exception { Loggers.RAFT.info("initializing Raft sub-system"); // 啓動Notifier,輪詢Datums,通知RaftListener executor.submit(notifier); // 獲取Raft集羣節點,更新到PeerSet中 peers.add(NamingProxy.getServers()); long start = System.currentTimeMillis(); // 從磁盤加載Datum和term數據進行數據恢復 RaftStore.load(); Loggers.RAFT.info("cache loaded, peer count: {}, datum count: {}, current term: {}", peers.size(), datums.size(), peers.getTerm()); while (true) { if (notifier.tasks.size() <= 0) { break; } Thread.sleep(1000L); System.out.println(notifier.tasks.size()); } Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start)); GlobalExecutor.register(new MasterElection()); // Leader選舉 GlobalExecutor.register1(new HeartBeat()); // Raft心跳 GlobalExecutor.register(new AddressServerUpdater(), GlobalExecutor.ADDRESS_SERVER_UPDATE_INTERVAL_MS); if (peers.size() > 0) { if (lock.tryLock(INIT_LOCK_TIME_SECONDS, TimeUnit.SECONDS)) { initialized = true; lock.unlock(); } } else { throw new Exception("peers is empty."); } Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}", GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS); }
在init方法主要作了以下幾件事:
其中,raft集羣內部節點間是經過暴露的Restful接口,代碼在 RaftController 中。
RaftController控制器是raft集羣內部節點間通訊使用的,具體的信息以下
POST HTTP://{ip:port}/v1/ns/raft/vote : 進行投票請求 POST HTTP://{ip:port}/v1/ns/raft/beat : Leader向Follower發送心跳信息 GET HTTP://{ip:port}/v1/ns/raft/peer : 獲取該節點的RaftPeer信息 PUT HTTP://{ip:port}/v1/ns/raft/datum/reload : 從新加載某日誌信息 POST HTTP://{ip:port}/v1/ns/raft/datum : Leader接收傳來的數據並存入 DELETE HTTP://{ip:port}/v1/ns/raft/datum : Leader接收傳來的數據刪除操做 GET HTTP://{ip:port}/v1/ns/raft/datum : 獲取該節點存儲的數據信息 GET HTTP://{ip:port}/v1/ns/raft/state : 獲取該節點的狀態信息{UP or DOWN} POST HTTP://{ip:port}/v1/ns/raft/datum/commit : Follower節點接收Leader傳來獲得數據存入操做 DELETE HTTP://{ip:port}/v1/ns/raft/datum : Follower節點接收Leader傳來的數據刪除操做 GET HTTP://{ip:port}/v1/ns/raft/leader : 獲取當前集羣的Leader節點信息 GET HTTP://{ip:port}/v1/ns/raft/listeners : 獲取當前Raft集羣的全部事件監聽者 RaftPeerSet
Raft中使用心跳機制來觸發leader選舉。心跳定時任務是在GlobalExecutor 中,
經過 GlobalExecutor.register(new HeartBeat())註冊心跳定時任務,具體操做包括:
public class HeartBeat implements Runnable { @Override public void run() { try { if (!peers.isReady()) { return; } RaftPeer local = peers.local(); local.heartbeatDueMs -= GlobalExecutor.TICK_PERIOD_MS; if (local.heartbeatDueMs > 0) { return; } local.resetHeartbeatDue(); sendBeat(); } catch (Exception e) { Loggers.RAFT.warn("[RAFT] error while sending beat {}", e); } } }
簡單說明了下Nacos中的Raft一致性實現,更詳細的流程,能夠下載源碼,查看 RaftCore 進行了解。源碼能夠經過如下地址檢出:
git clone https://github.com/alibaba/nacos.git
掃碼關注公衆號:架構進化論,得到第一手的技術資訊和原創文章