區塊鏈共識之Paxos演算法理解與實戰

chaors發表於2019-01-31

0.前言

本文記錄筆者學習和理解區塊鏈共識演算法Paxos的點滴,文章比較長,需要耐心來細細琢磨,筆者也是苦戰了一個週末才對此有那麼一點初步瞭解,有問題的地方請不吝斧正!

1.初始是閱讀本文後續內容的基礎,概念性的東西敘述不多,乾貨乾貨乾貨在後面的程式碼實戰。但有提供我認為優秀的帖子以供參考理解。前面這些Paxos基本的理解是2.程式碼設計和3.實戰流程的基礎!

1.初識

相關概念

Paxos 問題是指分散式的系統中存在故障(fault),但不存在惡意(corrupt)節點場景(即 可能訊息丟失或重複,但無錯誤訊息)下的共識達成(Consensus)問題。

Paxos最早是 Leslie Lamport 用 Paxon 島的故事模型來進行描述而命名。故事背景是古希臘 Paxon 島上的多個法官在一個大廳內對一個議案進行表決,如何達成統一 的結果。他們之間通過服務人員來傳遞紙條,但法官可能離開或進入大廳,服務人員可能偷 懶去睡覺。

Paxos 是第一個被證明的共識演算法,其原理基於兩階段提交併進行擴充套件。 作為現在共識演算法設計的鼻祖,以最初論文的難懂(演算法本身並不複雜)出名。演算法中將節點分為三種型別:

  • proposer:提出一個提案,等待大家批准為結案。往往是客戶端擔任該角色;

  • acceptor:負責對提案進行投票。往往是服務端擔任該角色;一般需要至少3個且節點個數為奇數,因為Paxos演算法最終要產生一個大多數決策者都同意的提議。

  • learner:被告知結案結果,並與之統一,不參與投票過程。可能為客戶端或服務端。

paxos演算法的兩階段

  • prepare 階段:
      1. Proposer 選擇一個提案編號 n,然後向acceptor的某個超過半數的子成員傳送編號為n的 prepare 請求;
      2. Acceptor 收到 prepare 訊息後,如果提案的編號n大於該acceptor已經回覆的所有 prepare 請求的編號,則 Acceptor 將自己上次已經批准的最大編號提案回覆給 Proposer,並承諾不再回復小於 n 的提案;

  • commit階段:
      1. 當一個 Proposer 收到了半數以上的 Acceptors 對 prepare 的回覆後,就進入批准階段。它要向回覆 prepare 請求的 Acceptors 傳送 accept 請求,包括編號 n 和根據 prepare階段 決定的 value。這裡的value是所有響應中編號最大的提案的value(如果根據 prepare 沒有已經接受的 value,那麼它可以自由決定 value)。
      2. 在不違背自己向其他 Proposer 的承諾的前提下,Acceptor 收到 accept 請求後即接受這個請求。即如果acceptor收到這個針對n提案的accept請求,只要該acceptor尚未對編號大於n的prepare請求做出過響應,它就可以通過這個提案。

他山之石

Paxos演算法初次接觸聽上去確實有點晦澀難懂,這裡有一篇貼子我覺得不錯。貼出來可以參考:

另外,wiki對Paxos的描述也是比較不錯和權威的參考資料。

有了以上對Paxos演算法的理解,我們才能進行下一步:自己程式設計實現Paxos演算法。

2.程式碼實戰

流程理解

Paxos演算法核心的兩個角色便是Proposer(提議者)和Acceptor(決策者),因此也必須圍繞這兩個物件進行演算法架構的設計。

Paxos演算法流程
Proposer行為分析
  • 1.0 向所有Acceptor發出一個提議(proposal);

  • 2.0 如果收到一個拒絕資訊(reject),嘗試重新傳送被拒絕的提議;

  • 2.1 如果收到一個Acceptor的承諾回應(agree),用一個標誌(agreeCount)來計數給了自己承諾的Acceptor個數。當agreeCount超過Acceptor總數的一半時,表示有大多數Acceptor承諾將接受這個提議,需要將自己的提議狀態置為承諾接受狀態(agreed)。同時,還要通知其他Proposer我這個提議已經得到大多數Acceptor承諾會接受。

  • 3.0 提議為承諾接受狀態(agreed)時,Proposer需要再向Acceptor集合傳送一個接受提議的確認請求,我們稱該請求為Accept請求。

  • 3.1 發出Accept請求後會收到Acceptor的回覆,如果收到接受資訊(Accept),用一個標誌(acceptCount)來計數接受自己提議的Acceptor個數。同樣當acceptCount超過半數時,表示大多數Acceptor接受了這個提議,需要將提議狀態由承諾接受狀態(agreed)置為接受狀態(acceptd)。
    同時,還要通知其他Proposer我這個提議已經得到大多數Acceptor接受。

  • 以上1,2屬於Paxos演算法的Prepare階段,3屬於Accept階段。

Acceptor行為分析
  • 1.0 當Acceptor收到一個提議後,判斷提議版本號是否大於自身儲存的提議版本。

  • 1.0 如果小於自身表示曾經已經給過別的Proposer承諾,傳送一個拒絕訊息(reject),表示自己拒絕給當前Proposer任何承諾。

  • 1.1 反之,則替換自身儲存的提議版本號並給當前Proposer傳送一個承諾回應(agree),表示將承諾接受他的提議。同時,將自身狀態置為已經給了某個Proposer承諾(agree)。

  • 2.0 Acceptor收到一個Proposer的編號為N的Accept請求,只要該Acceptor之前不曾承諾編號M(M>N)的其他Proposer提議,那麼他就接受該提案。同時,將自身狀態置為已接受某個Proposer提議,並通知所有Proposer這個訊息。

  • 以上1屬於Paxos演算法的Prepare階段,2屬於Accept階段。

以上行為分析針對本次Paxos演算法程式設計實戰!!!

類的設計

Paxos演算法解決的是分散式系統一致性的問題,我們通過埠號在一臺計算機上模擬多個節點。

毋庸置疑,我們分別需要一個Proposer類和Acceptor類。

PaxoProposer 提議者類
  • Proposer的作用是提出一個提議併傳送給Acceptor,所以他本身必須知道所有的Acceptor,同時有些時候要跟其他Proposer通訊,所以也需要知道所有的Proposer(見init方法)。

  • 基本的開始結束介面(start, stop)

  • 在判斷提議是否被大多數Acceptor承諾接受或最終接受,我們需要設定一個判定條件(getQuorumCount)

  • 當提議被承諾接受或最終接受時需要通知其他Proposer(notifyProposer)

  • 傳送訊息(提議或Accept請求)給Acceptor(sendMsg);接收來自Acceptor的訊息(recvMsg)

  • 為了方便除錯,我們可能需要知道整個過程請求提議的歷史記錄(getHistory)

  • 自己的提議最終被Acceptor接受的個數(getNumAccepted)

  • 清楚Paxos演算法流程後,我們發現假設有兩個Proposer依次提出編號遞增的提議,最終會陷入死迴圈使得Paxos演算法無法保證活性。所以,一般的做法是選取一個主Proposer作為領導,只有領導才能提出提議(setPrimary)。

  • Proposer類的一個難點在於提議發出後的各種狀態轉變與對應資料的處理。從提議發出到提議被接受整個過程,提議的狀態是在不斷地變化,但最終總會到達一個終止態。對於這種情況的處理,狀態機__註定是一個不錯的選擇。由於這裡有點複雜我們將提議功能單獨拿出來抽象為一個Proposer的協議類__PaxoProposerProtocol

-由於各個節點收發訊息是並行的,這裡對訊息的檢測需要用到執行緒。這裡HeartbeatListener來監聽訊息,HeartbeatSender用來傳送訊息。

"""
    @author: chaors

    @file: PaxoProposer.py

    @time: 2018/04/14 10:50

    @desc: 提議者
"""

class PaxoProposer:

    #心跳監聽類
    class HeartbeatListener(threading.Thread):
        pass
    #定時傳送類
    class HeartbeatSender(threading.Thread):
        pass
        

    #初始化
    def __init__(self, port, proposers=None, acceptors=None):
        pass
    #開始
    def start(self):
        pass

    #停止
    def stop(self):
        pass

    #設定是否為領導者
    def setPrimary(self, isPrimary):
        pass

    #獲取支援所有提議者的決策者
    def getGroup(self):
        pass

    #獲取所有提議者
    def getProposers(self):
        pass

    #獲取所有決策者
    def getAcceptors(self):
        pass

    #提議被承諾接受或最終接受的條件必須滿足:獲得1/2以上的Acceptor支援
    def getQuorumCount(self):
        pass

    #獲取本地記錄資料
    def getInstanceValue(self, instanceID):
        pass

    #獲取歷史記錄
    def getHistory(self):
        pass

    #獲取提議同意的數量
    def getNumAccepted(self):
        pass


    #通知其他提議者
    def notifyProposer(self, protocol, msg):
        pass


    #新的提議
    def newProposal(self, value, instance=None):
        pass

    # 傳送訊息
    def sendMsg(self, msg):
        pass

    # 接收訊息
    def recvMsg(self, msg):
        pass
複製程式碼
PaxoProposerProtocol類

用來提交一個提議,並用於提交提議後各種狀態的處理。

  • 定義一些狀態來表示當前Proposoer提議的各種狀態

  • 發起提議(propose)

  • 狀態機處理(doTranition)

"""
    @author: chaors

    @file: PaxoProposerProtocol.py

    @time: 2018/04/14 10:50

    @desc: 提議者協議
"""

class PaxoProposerProtocol:
    #常量
    STATE_UNDEFIND = -1  #提議協議未定義
    STATE_PROPOSED = 0  #提議型別
    STATE_REJECTED = 1  #拒絕狀態  提議被拒絕
    STATE_AGREED = 2    #提議被承諾接受 Prepare階段獲取大多數Acceptor承諾後的協議狀態
    STATE_ACCEPTED = 3  #提議被接受
    STATE_UNACCEPTED = 4  #提議未被拒絕

    def __init__(self, proposer):
        pass

    #發起提議
    def propose(self, value, pID, instanceID):
        pass

    #狀態過渡 根據狀態機執行
    def doTranition(self, msg):
        pass
複製程式碼
PaxoAcceptor類

決策者,對Proposer提出的提議和Accept請求做出回應。

  • 和Proposer類似的介面不再贅述。

  • 需要對比Proposer發來的提議版本(getHighestProposal)

"""
    @author: chaors

    @file: PaxoAcceptor.py

    @time: 2018/04/14 10:50

    @desc: 決策者
"""

class PaxoAcceptor:
    def __init__(self, port, proposers):
        pass

    #開始
    def start(self):
        pass

    #停止
    def stop(self):
        pass

    #失敗
    def fail(self):
        pass

    #恢復
    def recover(self):
        pass

    #傳送訊息
    def sendMsg(self, msg):
        pass

    #接收訊息
    def recvMsg(self, msg):
        pass


    #通知客戶端
    def notifyClient(self, protocol, msg):
        pass


    #獲取本地記錄資料
    def getInstanceValue(self, instanceID):
        pass

    #獲取最高同意建議
    def getHighestProposal(self, instanceID):
        pass
複製程式碼
PaxoAcceptorProtocol類

決策者協議,用來處理Proposer提出的提議,並同樣使用狀態機來處理自身各種狀態。

"""
    @author: chaors

    @file: PaxoAcceptorProtocol.py

    @time: 2018/04/14 10:50

    @desc: 決策者協議
"""

from Message import Message  #協議依賴訊息

class PaxoAcceptorProtocol:
    #常量
    STATE_UNDEFIND = -1  #協議未定義
    STATE_PROPOSAL_RECEIVED = 0  #收到訊息
    STATE_PROPOSAL_REJECTED = 1  #拒絕連結,網路不通可能
    STATE_PROPOSAL_AGREED = 2  #承諾將接受該提議  針對Proposer的PROPOSED請求
    STATE_PROPOSAL_ACCEPTED = 3  #接受該協議  針對Proposer的Accept請求
    STATE_PROPOSAL_UNACCEPTED = 4  #拒絕請求

    def __init__(self, client):
        pass

    #收到提議
    def recvProposal(self, msg):
        pass


    #過渡
    def doTranition(self, msg):
        pass

    #通知客戶端
    def notifyClient(self, msg):
        pass
複製程式碼
Message類

Proposer和Acceptor的角色都有了,還差一個他們之間傳遞的訊息類。這個訊息有以下幾種:

  • Proposer發出的提議請求
  • Proposer發出的Accept請求
  • Acceptor對提議請求的拒絕
  • Acceptor對提議請求的承諾
  • Acceptor對Accept請求的接受
  • Acceptor對Accept請求的不接受
  • 外部(Client)發給Proposer的提議
  • 作為對訊息的回覆訊息
  • 定時的心跳資訊,用來同步提議
"""
    @author: chaors

    @file: Message.py

    @time: 2018/04/14 09:31

    @desc: 訊息傳遞類
"""

class Message:
    #常量
    MSG_ACCEPTOR_AGREE = 0  #Acceptor對提議請求的承諾
    MSG_ACCEPTOR_ACCEPT = 1  #Acceptor對Accept請求的接受
    MSG_ACCEPTOR_REJECT = 2  #Acceptor對提議請求的拒絕
    MSG_ACCEPTOR_UNACCEPT = 3  #Acceptor對Accept請求的不接受
    MSG_ACCEPT = 4  #Proposer發出的Accept請求
    MSG_PROPOSE = 5  #Proposer發出的提議請求
    MSG_EXT_PROPOSE = 6  #外部(Client)發給Proposer的提議
    MSG_HEARTBEAT = 7  #定時的心跳資訊,用來同步提議

    def __init__(self, cmd=None):  #訊息初始化有個狀態
        pass
    
    #對某個訊息的回覆訊息
    def copyAsReply(self, msg):
        pass
複製程式碼
InstanceRecord類

提議被抽象在協議裡,在系統達到一致性之前,Proposer可能嘗試提交多次協議資訊(包含提議)。在Proposer和Acceptor之間都需要儲存所有的提議記錄,所以兩者都有一個InstanceRecord例項陣列。

對於Proposer,InstanceRecord例項陣列儲存的是提交過的所有提議記錄,並且會隨著提議狀態的改變更新記錄狀態(包括協議和記錄的值)的值。

對於Acceptor,InstanceRecord例項陣列儲存的是Acceptor接收的Proposer提議請求,並隨著提議版本的改變而更新。Acceptor給出承諾(agree)的條件是提議版本大於當前InstanceRecord裡的協議版本;Acceptor接受提議(accept)的條件是當前Accept請求版本號比之前給出承諾的的提議版本號大。

  • 協議,包含了每次請求的協議資訊(protocols)
  • 最高版本,當前所有提交的請求的最高版本(highestID)
  • 記錄值,該次請求的值
"""
    @author: chaors

    @file: InstanceRecord.py

    @time: 2018/04/14 10:31

    @desc: 本地記錄類,記錄決策者,提議者之間協議
"""

import threading, socket, pickle, queue,random
#  InstanceRecord本地記錄類,決策者,提議者之間協議
from PaxoProposerProtocol import PaxoProposerProtocol

class InstanceRecord():
    def __init__(self):
        self.protocols = {}  #協議字典
        self.highestID = (-1, -1)  #最高版本(提議版本,埠號)
        self.value = None  #提議值

    #增加協議
    def addProtocol(self, protocol):
        self.protocols[protocol.proposalID] = protocol
        #取得版本最高的協議  假設埠較大的Proposer為領導,優先承諾 埠相同時取版本號較大的
        if protocol.proposalID[1] > self.highestID[1] or 
                (protocol.proposalID[1] == self.highestID[1] 
                 and protocol.proposalID[0] > self.highestID[0]):
            self.highestID = protocol.proposalID

    #抓取協議
    def getProtocol(self, protocolID):

        return self.protocols[protocolID]

    #清理協議
    def cleanProtocols(self):
        keys = self.protocols.keys()  #取得所有可以
        #遍歷刪除協議
        for key in keys:
            protocol = self.protocols[key]
            if protocol.state == PaxoProposerProtocol.STATE_ACCEPTED:
                print("Deleting protocol")
                del self.protocols[key] #刪除協議
複製程式碼
MessagePump類

訊息的結構是有了,但是它是怎麼在節點(Proposer和Acceptor)之間傳遞的呢。這裡我們封裝一個基於Socket傳遞訊息的網路類。這裡接收訊息需要藉助一個執行緒,我們在構造一個接收訊息的輔助類。

這裡的只是不屬於Paxos演算法重點,就不贅述了。直接上程式碼。

"""
    @author: chaors

    @file: MessagePump.py

    @time: 2018/04/14 09:46

    @desc: 基於Socket傳遞訊息,封裝網路類傳遞訊息
"""

import threading  #執行緒
import pickle  #物件序列化
import socket  #網路資訊傳輸
import queue  #佇列

class MessagePump(threading.Thread):
    # 傳遞訊息的輔助類
    class MPHelper(threading.Thread):
        def __init__(self, owner):
            self.owner = owner  #傳遞訊息的物件的所有者

            threading.Thread.__init__(self)  # 父類初始化

        def run(self):  #執行
            while not self.owner.abort:  #只要所有者執行緒未結束
                try:
                    #返回二進位制資料,地址
                    (bytes, addr) = self.owner.socket.recvfrom(2048)  #收取訊息
                    msg = pickle.loads(bytes)  #讀取二進位制轉化為訊息
                    msg.source = addr[1]  #取出返回的地址
                    self.owner.queue.put(msg)  #訊息存入佇列

                except Exception as e:  #異常
                    print(e)

    def __init__(self, owner, port, timeout=2):
        #基本引數初始化
        self.owner = owner  #所有者
        self.timeout = 2  #超時時間
        self.port = port  #網路介面

        #網路通訊初始化
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #UDP通訊
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 200000)  #通訊引數
        self.socket.bind(("localhost", port))  #socket繫結
        self.socket.settimeout(timeout)  #設定超時

        self.queue = queue.Queue()  #佇列
        self.helper = MessagePump.MPHelper(self)  #接收訊息的工具類

        threading.Thread.__init__(self)  #父類初始化
        self.abort = False  #預設不終止狀態

    #執行主執行緒
    def run(self):
        self.helper.start()  #開啟收訊息的執行緒
        while not self.abort:  #只要不是終止狀態
            msg = self.waitForMsg()  #阻塞等待訊息
            self.owner.recvMsg(msg)  #收取訊息

    #等待訊息
    def waitForMsg(self):
        try:
            msg = self.queue.get(True, 3)  #從佇列中取訊息,最多等3s

            return msg
        except Exception as e:
            print(e)

            return None

    #傳送訊息
    def sendMsg(self, msg):
        bytes = pickle.dumps(msg)  #把訊息轉成二進位制
        addr = ("localhost", msg.to)
        self.socket.sendto(bytes, addr)  #傳送訊息到地址

        return True

    #設定狀態為放棄
    def doAbort(self):
        self.abort = True
複製程式碼
Paxos_MainTest Paxos演算法測試
"""
    @author: chaors

    @file: paxo_testMain.py

    @time: 2018/04/14 17:50

    @desc: Paxos演算法測試用例
"""

import threading, socket, pickle, queue,random
import time

from MessagePump import MessagePump
from Message import Message
from InstanceRecord import InstanceRecord
from PaxoProposer import PaxoProposer
from PaxoProposerProtocol import PaxoProposerProtocol
from PaxoAcceptorProtocol import PaxoAcceptorProtocol
from PaxoAcceptor import PaxoAcceptor

if __name__ == `__main__`:
    #Acceptor數量
    numclients = 5
    #例項化決策者陣列,決策者節點埠號為65520-65525
    acceptors = [PaxoAcceptor(port, [56321, 56322]) for port in range(65520, 65520 + numclients)]

    #例項化提議者,埠號分別56321,56322  對應的決策者為acceptors
    proposer1 = PaxoProposer(56321, [56321, 56322], [acceptor.port for acceptor in acceptors])
    proposer2 = PaxoProposer(56322, [56321, 56322], [acceptor.port for acceptor in acceptors])

    #啟動提議者提議程式
    proposer1.start()
    proposer1.setPrimary(True)
    proposer2.setPrimary(True)
    proposer2.start()

    #啟動決策者決策程式
    for acceptor in acceptors:
        acceptor.start()

    #模擬網路中兩個節點當機
    acceptors[0].fail()
    acceptors[1].fail()

    #利用Socket機制傳送提議給決策者
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    start = time.time()
    for i in range(1000):
        m = Message(Message.MSG_EXT_PROPOSE)
        m.value = 0 + i
        m.to = 56322  
        bytes = pickle.dumps(m)
        s.sendto(bytes, ("localhost", m.to))

        # if i == 2 or i == 30:
        #     print(leader2.getInstanceValue(1))

    #當提議被999個決策者接受時結束整個提議程式
    while proposer1.getNumAccepted() < 999:
        print(u"休眠1秒--被接受: %d" % proposer1.getNumAccepted())
        time.sleep(1)
    end = time.time()

    print(u"休眠10秒")
    time.sleep(10)
    print(u"結束領導者")
    proposer1.stop()
    proposer2.stop()
    print(u"結束客戶端")
    for acceptor in acceptors:
        acceptor.stop()

    print(u"領導者1 歷史記錄:  %s" % proposer1.getHistory())
    print(u"領導者 2 歷史記錄: %s " % proposer2.getHistory())
    print(u"一共用了%d 秒" % (end - start))
複製程式碼

3.通過程式碼進一步瞭解Paox演算法處理邏輯

上面已經完成了基本程式碼的架構,詳細原始碼稍後我會上傳到github。
接下來,我們通過一個簡單的測試用例來再一次更深入地從程式碼層面理解Paxos演算法的處理邏輯。

我們執行paxo_testMain程式碼,事先我在關鍵步驟處都打了斷點。這樣就可以完整地從程式碼角度看一次Paxos演算法兩個階段的執行,也能直觀地觀察到各個步驟的程式碼處理邏輯。

!!!閱讀說明:

#####1.x 對應Paxos演算法Prepare階段
#####2.x 對應Paxos演算法Commit階段

廢話少說上程式碼

  • 1.0 [start—1.0]Proposer收到一個訊息,訊息型別為一個提議。首先判斷自身是否為領導者,如果是建立協議
start---1.0
  • 1.1 [start—1.1]Proposer藉助自身的PaxoProposerProtocol例項發起一個提議請求
start---1.1_1
start---1.1_2有於訊息是併發執行的,這裡協議的狀態改變需要等到提議請求傳送傳送給所有的Acceptor後才會執行到這,所以start---1.1_1斷點之後可能不會是這個斷點
  • 1.2 [start—1.2]Acceptor收到一個訊息,訊息型別為提議。然後藉助AcceptorProtocol例項處理提議。
start---1.2
  • 1.3[start—1.3] AcceptorProtocol收到一個提議,判斷提議版本回復Proposer承諾接受訊息或拒絕訊息
start---1.3
  • 1.4[start—1.4]Proposer收到一個訊息,型別為Acceptor的承諾(MSG_ACCEPTOR_AGREE)。既然不是Proposer最終要的接受提議的結果,轉給ProposerPropotocal(當前訊息的記錄ID(instanceID)對應的協議)狀態機處理。
start---1.4
  • [start—1.5] and [start—2.0]
–[start—1.5] ProposerPropotocal狀態機函式收到一個MSG_ACCEPTOR_AGREE訊息,此時表示新增加一個Acceptor承諾會接受我的請求。
— [start—2.0_1]該條件下的程式碼會不斷執行,直到許諾Proposer的數量超過半數,表示Prepare階段基本結束。此時協議狀態更新為協議被承諾接受(STATE_AGREED)。此時的Proposer向Acceptor集合傳送Accept請求,請求Acceptor確認他們的許諾。
— [start—2.0_2]同時,向其他Proposer廣播該訊息,使得其他Proposer知道Prepare階段哪個提議獲得的承諾最多。這樣,在Commit階段,他們可能通過改變提議來使系統儘快達到一致性。
start---1.5 & 2.0
  • 2.1[start—2.1]Acceptor收到一個訊息,型別為來自Proposer的Accept請求。藉助AcceptorPropotal處理該訊息。
start---2.1
  • 2.2[start—2.2]
    — [start—2.2_1] AcceptorPropotal收到Proposer發出的Accept請求。按Paxos演算法思想,這裡需要判斷請求版本號,當且僅當Acceptor之前承諾過的提議版本號最大值小於Accept請求版本號才會接受該Proposer提議。這裡我們藉助“先入為主”的思想簡化問題,只要這時候協議狀態為STATE_PROPOSAL_AGREED,就給所有Proposer廣播訊息表示自己確認接受該Proposer提議。
    — [start—2.2_2] AcceptorPropotal通知Acceptor更新InstanceRecord的值,到此時已有一個提議被一個Acceptor最終接受。
start---2.2
  • 2.3[start—2.3]Proposer收到一個訊息,型別為Acceptor確認接受提議(MSG_ACCEPTOR_ACCEPT),根據該訊息更新Proposer的InstanceRecord(新建record,追加協議等)。並將訊息交給ProposerProtocol狀態機處理。
start---2.3
  • 2.4[start—2.4]
    [start—2.4_1] — ProposerProtocol收到一個Acceptor的最終確認訊息(MSG_ACCEPTOR_ACCEPT),此時表示新增一個Acceptor最終接受了我的提議。但此時的協議狀態仍然是STATE_AGREED狀態,因為一個提議最終被系統接受必須先被超半數的Acceptor節點確認接受。
    [start—2.4_2] STATE_AGREED條件下的程式碼會不斷執行,直到最終接受提議的Acceptor超過半數。這時,協議狀態由STATE_AGREED狀態更新為最終被系統確認狀態(STATE_ACCEPTED)。最後,當前Proposer更新自己的InstanceRecord記錄。當然,這裡也有一種可能是被超過半數節點不接受,那麼同樣其他Proposer節點必有一個節點提議被接受。
start---2.4
  • 2.5[start—2.5] Proposer更新InstanceRecord記錄,如果協議最終被大多數Acceptor拒絕則嘗試重新提議(步驟回到1.1)。
start---2.5

#總結
以上,我們就從程式碼層面對PAxos演算法有一個更深入的瞭解,我想根據程式碼再反過來理解PAxos演算法,勢必會有一個更深刻的印象。

剛開始聽說Paxos也是好幾臉懵逼,也是鏖戰一個週末才有這麼點體悟。還在學習區塊鏈的小小白起步中,寫這篇帖子也是記錄下自己學習的過程。勉之。

原始碼點這裡

碼農:

擇而為農

請碼好你的碼

一碼不調

何以碼天下

網際網路顛覆世界,區塊鏈顛覆網際網路!

————————————————–20180416 00:16

相關文章