PAXOS---最重要的分佈式算法----簡單模擬


最近由於某種原因需要學習分佈式系統,其中涉及到分佈式系統中的核心內容:PAXOS算法,據說此算法是分佈式系統的基石,所有的分佈式系統都是在此協議下進行的,是非常重要的分佈式算法,用來保證系統的容錯性和一致性的。

關於PAXOS的歷史和傳奇故事有很多,wiki上大把,摘錄一段,讓大家也瞭解一下這個協議:

分佈式系統中的節點通信存在兩種模型:共享內存(Shared memory)和消息傳遞(Messages passing)。基於消息傳遞通信模型的分佈式系統,不可避免的會發生以下錯誤:進程可能會慢、垮、重啓,消息可能會延遲、丟失、重複,在基礎 Paxos 場景中,先不考慮可能出現消息篡改即拜占庭錯誤的情況。

Paxos 算法解決的問題是在一個可能發生上述異常的分佈式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。一個典型的場景是,在一個分佈式數據庫系統中,如果各節點的初始狀態一致,每個節點都執行相同的操作序列,那麼他們最後能得到一個一致的狀態。爲保證每個節點執行相同的命令序列,需要在每一條指令上執行一個“一致性算法”以保證每個節點看到的指令一致。一個通用的一致性算法可以應用在許多場景中,是分佈式計算中的重要問題。因此從20世紀80年代起對於一致性算法的研究就沒有停止過。

爲描述 Paxos 算法,Lamport 虛擬了一個叫做 Paxos 的希臘城邦,這個島按照議會民主制的政治模式制訂法律,但是沒有人願意將自己的全部時間和精力放在這種事情上。所以無論是議員,議長或者傳遞紙條的服務員都不能承諾別人需要時一定會出現,也無法承諾批准決議或者傳遞消息的時間。但是這裏假設沒有拜占庭將軍問題(Byzantine failure,即雖然有可能一個消息被傳遞了兩次,但是絕對不會出現錯誤的消息);只要等待足夠的時間,消息就會被傳到。另外,Paxos 島上的議員是不會反對其他議員提出的決議的。

對應於分佈式系統,議員對應於各個節點,制定的法律對應於系統的狀態。各個節點需要進入一個一致的狀態,例如在獨立Cache的對稱多處理器系統中,各個處理器讀內存的某個字節時,必須讀到同樣的一個值,否則系統就違背了一致性的要求。一致性要求對應於法律條文只能有一個版本。議員和服務員的不確定性對應於節點和消息傳遞通道的不可靠性。

其中提到的Lamport是個計算機大牛,有興趣的可以自行google

對於PAXOS算法的具體描述就不說了,隨便搜索都一大把,但是大部分都看不太明白,推薦一個blog地址:

按這個描述,可以比較清楚的搞清楚該算法的含義和精髓,當然,可以擴展閱讀該作者更多的文章。哈哈

模擬算法中分爲三個部分
Leader,這是個輔助線程,用來分發議案的編號等信息
Proposer,這個是議案的提出者,負責提出議案的
Acceptor,這個是議案的響應者,負責表決通過議案的
模擬程序中沒有learner,也就是學習者,這個不是必要的
proposer和acceptor實際上是可以兼職的,但是爲了簡單,我們把他們分開來,各司其職


對於一個proposer,他要做的工作其實可以簡單描述如下:

1.從leader那裏拿到議案的編號和議案的內容
2.發送議案到不少於acceptor總數一半的響應者中,並等待響應者的迴應
3.在一個給定的超時時間內,儘可能的收集響應者的迴應
4.分析迴應
    1)如果全部迴應是chosen,直接通過議案,結束
    2)如果有迴應是reject,直接停止議案,結束
    3)如果是其他情況,根據迴應的議案內容重新生成議案,但是議案的編號不變,再發給所有人。
    4)如果有人的迴應由於總總原因沒有收到,就再選擇一個新的迴應者,發送議案
5)回到2繼續發送議案,但是此時議案在4的過程中可能已經換了,但是議案的編號並沒有變


而對於一個acceptor,工作更爲簡單

1.首先保存一個數據 (s,v,sh) 其中s是上次通過的決議編號,v是通過的決議編號的內容,sh是承諾的最小決議編號,小於這個數的編號直接拒絕。
2.接收proposer過來的數據
3.分析數據:
    1) 如果(s,v,sh)都是0,將接收的數據填入到該數據結構中
    2)如果接收的數據編號小於sh,直接拒絕
    3)如果接收的數據編號大於sh,並且s不爲0,通過議案,並要告訴對方你曾經同意過 s,v 的議案
    4)如果接收的數據編號等於sh,直接通過
4跳轉到2


上面就是該算法的簡單描述,當然,在實際中還有很多其他問題需要挨個討論,目前,我們只是模擬一下這個算法,思想上正確就行了。代碼也挺簡單,貼一些,全部的代碼github上自取,用處不大,只是簡單的模擬一下 

proposer處理報文
if(self.start_propose==True and time.time()-self.time_start > 5):
                    printStr(self.name +"的本輪決議"+self.value+"投票結束,同意:"+str(self.accept)+"拒絕:"+str(self.reject) + "選擇:"+str(self.chosen))
                    self.start_propose=False
                    if(self.reject>0):
                        printStr(self.name+"的決議"+self.value+"被否決,停止提議,退出")
                    if(self.chosen==len(self.acceptors)):
                        printStr(" "+self.name+"的決議"+self.value+"被同意,完成決議過程 ")
                    if (self.accept>0 or
                       (self.chosen<len(self.acceptors) and self.chosen>0 and self.reject==0) or
                       (self.accept==0 and self.chosen==0 and self.reject==0)):
                        self.reject=0
                        self.chosen=0
                        self.accept=0
                        self.sendPropose() 



acceptor處理報文片段
    
###############################################
    #
    #處理議案提出者提出的決議
    #
    ###############################################
    def processPropose(self,value):
        res={}
        #如果從來沒接收過議案,跟新自身議案
        if(0==self.values["max"] and 0==self.values["last"]):
            self.values["max"]=value["Vnum"]
            self.values["last"]=value["Vnum"]
            self.values["value"]=value["Value"]
            res={
                 "type":"accpting",
                 "result":"accept",
                 "last":0,
                 "value":self.values["value"],
                 "accpetor":self.num,
                 "time":value["time"]}
        else:
            #如果收到的議案大於承諾最低表決的議案,同意並告知之前表決結果
            if(self.values["max"] < value["Vnum"]):
                self.values["max"]=value["Vnum"]
                res={
                    "type":"accpting",
                    "result":"accept",
                     "last":self.values["last"],
                     "value":self.values["value"],
                     "accpetor":self.num ,
                     "time":value["time"]}
            else:
                #如果收到的議案等於承諾最低表決的議案,完全同意議案,表決結束
                if(self.values["max"] == value["Vnum"]):
                    
                    self.values["last"]=value["Vnum"]
                    self.values["value"]=value["Value"]
                    res={
                        "type":"accpting",
                        "result":"chosen",
                        "last":self.values["last"],
                        "value":self.values["value"],
                        "accpetor":self.num,
                        "time":value["time"]
                     }
                else:
                    #如果收到的議案小於承諾最低表決的議案,直接拒絕
                    res={
                        "type":"accpting",
                        "result":"reject",
                        "last":self.values["last"],
                        "value":self.values["value"],
                        "accpetor":self.num,
                        "time":value["time"]
                     }
        return res



具體代碼請移步github
https://github.com/wyh267/PAXOS


===========================================================================================
歡迎大家關注我的微信號:XJJ267    西加加語言
或者掃描下面的二維碼也行哦。



發佈了54 篇原創文章 · 獲贊 16 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章