日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Raft 协议(附完整实现源码)

發(fā)布時(shí)間:2024/8/5 综合教程 39 生活家
生活随笔 收集整理的這篇文章主要介紹了 Raft 协议(附完整实现源码) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Paxos 存在的問題

Paxos 算法的描述偏學(xué)術(shù)化,缺失了很多細(xì)節(jié),無法直接應(yīng)用于工程領(lǐng)域。實(shí)際工程應(yīng)用中的分布式算法大多是 Paxos 的變種,驗(yàn)證這些算法的正確性也成為了一個(gè)難題。

舉個(gè)例子:上一篇文章的 最后 介紹了一個(gè)應(yīng)用 Paxos 算法的工程模型,這個(gè)模型存在明顯的寫性能瓶頸:

使用多主架構(gòu),寫入沖突的概率高
每次更新操作都需要至少 2 輪以上的網(wǎng)絡(luò)通信,通信開銷大

如果要提高該模型的性能,仍需要在很多細(xì)節(jié)上做進(jìn)一步調(diào)整,最終實(shí)現(xiàn)出來的算法已經(jīng)和原始的版本的 Paxos 相去甚遠(yuǎn)。

為了解決以上問題,另一個(gè)高性能且易于理解的一致性算法橫空出世:Raft

在學(xué)習(xí)算法的過程中,使用 Java 實(shí)現(xiàn)了一個(gè)功能完善的 Raft 協(xié)議:rafting

代碼忠實(shí)于論文原文,包含了其中的眾多算法細(xì)節(jié),希望對各位學(xué)習(xí) Raft 的朋友有所幫助

基本概念

Raft 算法基于 復(fù)制狀態(tài)機(jī)Replicated State Machine模型,本質(zhì)上就是一個(gè)管理 日志復(fù)制 的算法。

Raft 集群采用 Single Leader 架構(gòu),集群中有唯一的 Leader 進(jìn)程負(fù)責(zé)管理日志復(fù)制,其職責(zé)有:

接受 Client 發(fā)送的請求
將日志記錄同步到其他進(jìn)程
告知其他進(jìn)程的何時(shí)能夠提交日志

復(fù)制狀態(tài)機(jī)

復(fù)制狀態(tài)機(jī)的本質(zhì)就是:Paxos + WAL

每個(gè)進(jìn)程維護(hù)一個(gè)狀態(tài)機(jī)State Machine,并且使用一個(gè)日志存儲其所要執(zhí)行指令。
如果兩個(gè)狀態(tài)機(jī)執(zhí)行按照相同的順序,執(zhí)行相同的指令,則這兩個(gè)進(jìn)程最終能夠收斂到同個(gè)狀態(tài)。如果能保證所有進(jìn)程的日志一致,則每個(gè)進(jìn)程的狀態(tài)必定也是一致的。

任期

為了減少不必要的網(wǎng)絡(luò)通信,日志追加順序由集群唯一的 Leader 決定,無須與其他節(jié)點(diǎn)協(xié)商。通信開銷從最低 2 次降為固定的 1 次,從而大幅提高了算法的性能。

出于可用性考慮,當(dāng)前 Leader 下線后,集群需要從存活的節(jié)點(diǎn)中挑選一個(gè)新的 Leader,這個(gè)過程被稱為選舉 election

每次選舉都會產(chǎn)生一個(gè)新的任期號 term(單調(diào)遞增),如果選舉中產(chǎn)生了一個(gè)新的 Leader,那么這個(gè)任期號會伴隨這個(gè) Leader 直到其下線。

每個(gè) 參與者 進(jìn)程都會維護(hù)一個(gè) current_term 用于表示已知的最新任期,進(jìn)程之間通過彼此交換該值來感知 Leader 變化。

/**
 * 基礎(chǔ)信息
 */
public abstract class RaftMember implements RaftParticipant {

    // 響應(yīng) RPC 前需要持久化以下兩個(gè)屬性
    protected final long currentTerm; // 已知的最新的 term(初始為 0)
    protected final ID lastCandidate; // 最近一次贊成投票的 candidate

    protected RaftMember(long term, ID candidate) {
        this.currentTerm = term;
        this.lastCandidate = candidate;
        stableStorage().persist(currentTerm, lastCandidate);
    }

    /**
     *  @see RaftParticipant#currentTerm()
     */
    @Override
    public long currentTerm() {
        return currentTerm;
    }

    /**
     *  @see RaftParticipant#votedFor()
     */
    @Override
    public ID votedFor() {
        return lastCandidate;
    }

}

日志

日志是 Raft 的核心概念。Raft 保證日志是連續(xù)且一致的,并且最終能夠被所有進(jìn)程按照日志索引的順序提交。

每條日志記錄包含:

任期term:生成該條記錄的 Leader 對應(yīng)的任期
索引index:其在日志中的順序
命令command:可執(zhí)行的狀態(tài)機(jī)指令

一旦某條日志中的命令被狀態(tài)機(jī)執(zhí)行了,那么我們稱這條記錄為已提交committed,Raft 保證已提交的記錄不會丟失。

角色

Raft集群中每個(gè)進(jìn)程只能擔(dān)任其中一個(gè)角色:

Leader:發(fā)送心跳、管理日志復(fù)制與提交
Follower:被動響應(yīng)其他節(jié)點(diǎn)發(fā)送過來的請求
Candidate:主動發(fā)起并參與選舉

Raft 進(jìn)程間使用 RPC 的方式進(jìn)行通信,實(shí)現(xiàn)最基礎(chǔ)的共識算法只需 兩種RPC:

RequestVote:用于選舉產(chǎn)生 Leader
AppendEntries:復(fù)制日志與發(fā)送心跳

/**
 * RPC 接口
 * */
public interface RaftService {

    /**
     * 復(fù)制日志+發(fā)送心跳(由 leader 調(diào)用)
     * @param term leader 任期
     * @param leaderId leader 在集群中的唯一標(biāo)識
     * @param prevLogIndex 緊接在新的之前的日志條目索引
     * @param prevLogTerm prevLogIndex 對應(yīng)的任期
     * @param entries 日志條目(發(fā)送心跳時(shí)為空)
     * @param leaderCommit leader 已經(jīng)提交的日志條目索引
     * @return 當(dāng) follower 中的日志包含 prevLogIndex 與 prevLogTerm 匹配的日志條目返回 true
     * */
    Async<RaftResponse> appendEntries(
            long term, ID leaderId,
            long prevLogIndex, long prevLogTerm,
            Entry[] entries, long leaderCommit) throws Exception;

    /**
     * 選主(由 candidate 調(diào)用)
     * @param term candidate 任期
     * @param candidateId candidate 在集群中的唯一標(biāo)識
     * @param lastLogIndex candidate 最后一條日志條目的索引
     * @param lastLogTerm  candidate 最后一條日志條目的任期
     * @return 當(dāng)收到贊成票時(shí)返回 true
     * */
    Async<RaftResponse> requestVote(
            long term, ID candidateId,
            long lastLogIndex, long lastLogTerm) throws Exception;

}

算法流程

基于 Single Leader 模型,Raft 將一致性問題分解為 3 個(gè)獨(dú)立的子問題:

Leader 選舉Election:Leader 進(jìn)程失效后能夠自動選舉出一個(gè)新的 Leader
日志復(fù)制Replication:Leader 保證其他節(jié)點(diǎn)的日志與其保持一致
狀態(tài)安全 Safety:Leader 保證狀態(tài)機(jī)執(zhí)行指令的順序與內(nèi)容完全一致

為了方便理解,下面結(jié)合 動畫 進(jìn)行介紹。

選舉

使用 心跳超時(shí)heartbeat timeout機(jī)制來觸發(fā) Leader 選舉:

節(jié)點(diǎn)啟動時(shí)默認(rèn)處于 Follower 狀態(tài),如果 Follower 超時(shí)未收到 Leader 心跳信息,會轉(zhuǎn)換為 Candidate 并向其他節(jié)點(diǎn)發(fā)起 RequestVote 請求。

當(dāng) Candidate 收到半數(shù)以上的選票之后成為 Leader,開始定時(shí)向其他節(jié)點(diǎn)發(fā)起 AppendEntries 請求以維持其 Leader 的地位。

Leader 失效之后停止發(fā)送心跳,F(xiàn)ollower 的心跳超時(shí)機(jī)制又會被觸發(fā),開始新一輪的選舉。

復(fù)制

未提交日志
已提交日志

集群中只有 Leader 對外提供服務(wù):

客戶端與 Leader 進(jìn)行通信時(shí),每個(gè)請求包含一條可以被狀態(tài)機(jī)執(zhí)行的命令。

當(dāng) Leader 在接收到命令之后,首先會將命令轉(zhuǎn)換為一條對應(yīng)的 日志記錄log entry,并追加到本地的日志中。然后調(diào)用 AppendEntries 將這條日志復(fù)制到其他節(jié)點(diǎn)的日志中。

當(dāng)日志被復(fù)制到過半數(shù)節(jié)點(diǎn)上時(shí),Leader 會將這條日志中包含的命令 提交commit狀態(tài)機(jī)執(zhí)行,最后將執(zhí)行結(jié)果告知客戶端。

網(wǎng)絡(luò)分區(qū)

使用 過半數(shù)majority機(jī)制來處理腦裂:

發(fā)生網(wǎng)絡(luò)分區(qū)后,集群中可能同時(shí)出現(xiàn)多個(gè) Leader,復(fù)制機(jī)制保證了最多只有一個(gè) Leader 能夠正常對外提供服務(wù)。
如果日志無法復(fù)制到多數(shù)節(jié)點(diǎn),Leader 會拒絕提交這些日志,當(dāng)網(wǎng)絡(luò)分區(qū)消失后,集群會自動恢復(fù)到一致的狀態(tài)。

安全性保證

選舉時(shí)…

保證新的 Leader 擁有所有已經(jīng)提交的日志

每個(gè) Follower 節(jié)點(diǎn)在投票時(shí)會檢查 Candidate 的日志索引,并拒絕為日志不完整的 Candidate 投贊成票
半數(shù)以上的 Follower 節(jié)點(diǎn)都投了贊成票,意味著 Candidate 中包含了所有可能已經(jīng)被提交的日志

提交日志時(shí)…

Leader 只主動提交自己任期內(nèi)產(chǎn)生的日志

如果記錄是當(dāng)前 Leader 所創(chuàng)建的,那么當(dāng)這條記錄被復(fù)制到大多數(shù)節(jié)點(diǎn)上時(shí),Leader 就可以提交這條記錄以及之前的記錄
如果記錄是之前 Leader 所創(chuàng)建的,則只有當(dāng)前 Leader 創(chuàng)建的記錄被提交后,才能提交這些由之前 Leader 創(chuàng)建的日志

總結(jié)

一致性算法的本質(zhì):一致性與可用性之間的權(quán)衡

Raft 的優(yōu)點(diǎn):Single Leader 的架構(gòu)簡化日志管理

所有日志都由 Leader 流向其他節(jié)點(diǎn),無需與其他節(jié)點(diǎn)進(jìn)行協(xié)商。其他節(jié)點(diǎn)只需要記錄并應(yīng)用 Leader 發(fā)送過來日志內(nèi)容即可,將原來的兩階段請求優(yōu)化為一次 RPC 調(diào)用。

Raft 的缺點(diǎn):對日志的連續(xù)性有較高要求

為了簡化日志管理,Raft 的日志不允許存在空隙,限制了并發(fā)性。某些應(yīng)用場景下,需要通過Multi-Raft的模式對無關(guān)的業(yè)務(wù)進(jìn)行解耦,從而提高系統(tǒng)的并發(fā)度。

總結(jié)

以上是生活随笔為你收集整理的Raft 协议(附完整实现源码)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。