作者:吳香偉 發表於 2014/09/11
版權聲明:可以任意轉載,轉載時務必以超鏈接形式標明文章原始出處和作者信息以及版權聲明
Paxos算法存在活鎖問題。從節點中選出Leader,然后將所有對數據的修改都通過Leader作為提案提出,可以讓算法快速收斂。Leader的選舉規則是,由當前活動的Monitor節點中rank值最小的節點當選。選舉不僅會產生Leader還將確定Quorum成員,Quorum成員就是那些支持新Leader節點當選Leader的節點。因此,雖然不能保證Leader的rank值是所有節點中最小的,但是可以保證它的值在Quorum中是最小的。Quorum是Monitor中的多數派,也就是說它的成員數目必須大於N/2+1,N為Monitor節點數目。
Leader選舉過程大致可以分成以下3個步驟:首先,Proposer提出提案,發送propose消息給所有的Monitor節點。其次,Monitor節點(Acceptor)接收到proposer消息,如果接受提案則回復ack消息。最后,Proposer統計接收到的ack消息,向其它節點發送victory消息宣布贏得Leader選舉並同步數據。
提出propose消息
在選舉Leader過程中,epoch擔當非常重要的角色。它有以下幾個方面的作用:
- 代表邏輯時間。正常情況下,Quorum中各個節點的epoch值應該是相等的。當節點離線后,它的epoch值被保存在數據庫中,重新上線后它的epoch值比其它節點的小。在處理propose消息時,Acceptor節點根據epoch值來判斷消息是不是最新的。
- 用於判斷當前節點是否處於Leader選舉狀態。當epoch為奇數時,說明節點處於選舉狀態;選舉結束后,epoch值會遞增為偶數並同步到所有的Quorum成員。
Proposer提出選舉提案前,先從數據庫中讀取epoch值,並將其遞增到奇數。
處理propose消息
Acceptor處理propose消息時,主要考慮epoch(代表時間)和rank兩個因素:首先,判斷propose消息是否是由於網絡延遲導致的舊提案,若是則拒絕提案;其次,判斷proposer的rank值是否是它知道的rank中最小的,若是則接受並且承諾不接受rank值比提案的rank值更大的提案。
處理ack消息
Ack消息對應Paxos的Accept消息。
Ack消息有兩個作用:一種情況是通知Proposer它的提案被發送ack消息的Acceptor接受了;另一種情況是,Acceptor已經接受了提案(因為它的rank值小於Acceptor),但是存在提案號更高的提案,Proposer應該放棄當前的提案,使用更高的提案號重新提出提案。
例子
假設原來已經存在兩個節點A和B,現在加入新節點C,並且節點C的rank值是三者中最小的。節點C加入時,將引發選舉:
- 節點C向其余兩個節點發送propose消息,將自己設置為Leader。由於節點C是新加入的,所以它的epoch值從0開始遞增;
- 節點A、B接受到消息后,發現節點C的epoch值比較舊。但由於節點C不在Quorum中,說明這個提案不是由於網絡延遲導致的舊提案。並且節點C的rank值比自己小,所以接受提案並提醒對方更新epoch值。
- 節點C接收到節點A和B的ack消息,發現自己的epoch值比它們都小,所以用它們的epoch更新自己,並重新發送propose消息,提出提案。
- 節點A、B接受到新propose消息,發現節點C的epoch值比自己的新,於是更新自己的epoch值(持久化到磁盤)。另外,節點C的rank值也比自己的小,所以接受提案。
- 節點C接收到節點A和B對新提案的Ack消息,贏得選舉。
- 節點C向其余節點發送victory消息,更新Quorum以及Leaders角色。
如果新加入節點的rank值的不是最小的,那么當它的proposer消息發送到rank值比自己更小的節點時,會引發節點提出提案參加候選。這種情況下,新節點雖然不會贏得選舉,但是能夠讓自己加入到Quorum多數派。
疑問:多Leader情況
假設有A到E共5個節點,它們的rank值是依次遞增的。另外,A和B之間相互不通。當5個節點的epoch都相同,節點A和B同時提出提案(此時提案的epoch也相同)。
-----------------------
A B C D E
A + - + + +
B - + + + +
-----------------------
如果B的propose先達到C、D和E節點,那么這三個節點都會接受B節點的提案。這樣,節點B就獲得了多數派的支持,選舉超時后它將贏得選舉。當節點A的propose消息到達C、D和E節點時,由於A節點的rank值要比它們已經接受的B節點的rank值小,所以這三個節點將接受A節點的提案。
這樣,節點A和節點B都認為贏得了選舉,都將自己初始化為Leader,都可以接受來自客戶端對Cluster map的修改。
這種情況下,如果節點A的propose消息早於B節點的消息到達C、D、E中兩個或兩個以上的節點時,B節點將不能贏得選舉。因為Acceptor承諾不接受rank值更大的提案,B節點后達到時將被拒絕。
其它
變量說明
start_stamp: 提出提案的時間戳
acked_me: 回復ack消息的節點集合
electing_me: “選我”,標記自己為候選人
leader_acked: 接受的提案的內容,沒接受提案時該值默認為-1;
接口說明
1、選舉的入口函數為Monitor::start_election(),進入該函數時將Monitor的狀態更改為STATE_ELECTING。Elector::start()為private方法,必須經過Elector::call_election()方法調用;
2、選舉成功后進入Monitor::win_election()函數將Monitor狀態修改為STATE_LEADER,選舉失敗的節點進入Monitor::lose_election()函數將Monitor的狀態修改為STATE_PEON;
參考資料