本文主要介紹zookeeper中zookeeper Server leader的選舉,zookeeper在選舉leader的時(shí)候采用了paxos算法(主要是fast paxos),這里主要介紹其中兩種:LeaderElection 和FastLeaderElection.
我們先要清楚以下幾點(diǎn)
- 一個(gè)Server是如何知道其它的Server
在zookeeper中,一個(gè)zookeeper集群有多少個(gè)Server是固定,每個(gè)Server用于選舉的IP和PORT都在配置文件中
- 除了IP和PORT能標(biāo)識一個(gè)Server外,還有沒有別的方法
每一個(gè)Server都有一個(gè)數(shù)字編號,而且是唯一的,我們根據(jù)配置文件中的配置來對每一個(gè)Server進(jìn)行編號,這一步在部署時(shí)需要人工去做,需要在存儲數(shù)據(jù)文件的目錄中創(chuàng)建一個(gè)文件叫myid的文件,并寫入自己的編號,這個(gè)編號在處理我提交的value相同很有用
獲得n/2 + 1個(gè)Server同意(這里意思是n/2 + 1個(gè)Server要同意擁有zxid是所有Server最大的哪個(gè)Server)
zookeeper中選舉主要是采用UDP,也一種實(shí)現(xiàn)是采用TCP,在這里介紹的兩種實(shí)現(xiàn)采用的是UDP
LOOKING 初始化狀態(tài)
LEADING 領(lǐng)導(dǎo)者狀態(tài)
FOLLOWING 跟隨者狀態(tài)
- 如果所有zxid都相同(例如: 剛初始化時(shí)),此時(shí)有可能不能形成n/2+1個(gè)Server,怎么辦
zookeeper中每一個(gè)Server都有一個(gè)ID,這個(gè)ID是不重復(fù)的,而且按大小排序,如果遇到這樣的情況時(shí),zookeeper就推薦ID最大的哪個(gè)Server作為Leader
- zookeeper中Leader怎么知道Fllower還存活,F(xiàn)llower怎么知道Leader還存活
Leader定時(shí)向Fllower發(fā)ping消息,F(xiàn)llower定時(shí)向Leader發(fā)ping消息,當(dāng)發(fā)現(xiàn)Leader無法ping通時(shí),就改變自己的狀態(tài)(LOOKING),發(fā)起新的一輪選舉
名詞解釋
zookeeer Server: zookeeper中一個(gè)Server,以下簡稱Server
zxid(zookeeper transtion id): zookeeper 事務(wù)id,他是選舉過程中能否成為leader的關(guān)鍵因素,它決定當(dāng)前Server要將自己這一票投給誰(也就是我在選舉過程中的value,這只是其中一個(gè),還有id)
myid/id(zookeeper server id): zookeeper server id ,他也是能否成為leader的一個(gè)因素
epoch/logicalclock:他主要用于描述leader是否已經(jīng)改變,每一個(gè)Server中啟動(dòng)都會有一個(gè)epoch,初始值為0,當(dāng) 開始新的一次選舉時(shí)epoch加1,選舉完成時(shí) epoch加1。
tag/sequencer:消息編號
xid:隨機(jī)生成的一個(gè)數(shù)字,跟epoch功能相同
Fast Paxos消息流向圖與Basic Paxos的對比
消息流向圖
Client Proposer Acceptor Learner
| | | | | | |
X-------->| | | | | | Request
| X--------->|->|->| | | Prepare(N)//向所有Server提議
| |<---------X--X--X | | Promise(N,{Va,Vb,Vc})//向提議人回復(fù)是否接受提議(如果不接受回到上一步)
| X--------->|->|->| | | Accept!(N,Vn)//向所有人發(fā)送接受提議消息
| |<---------X--X--X------>|->| Accepted(N,Vn)//向提議人回復(fù)自己已經(jīng)接受提議)
|<---------------------------------X--X Response
| | | | | | |
沒有沖突的選舉過程
Client Leader Acceptor Learner
| | | | | | | |
| X--------->|->|->|->| | | Any(N,I,Recovery)
| | | | | | | |
X------------------->|->|->|->| | | Accept!(N,I,W)//向所有Server提議,所有Server收到消息后,接受提議
| |<---------X--X--X--X------>|->| Accepted(N,I,W)//向提議人發(fā)送接受提議的消息
|<------------------------------------X--X Response(W)
| | | | | | | |
第一種實(shí)現(xiàn): LeaderElection
LeaderElection是Fast paxos最簡單的一種實(shí)現(xiàn),每個(gè)Server啟動(dòng)以后都詢問其它的Server它要投票給誰,收到所有Server回復(fù)以后,就計(jì)算出zxid最大的哪個(gè)Server,并將這個(gè)Server相關(guān)信息設(shè)置成下一次要投票的Server
每個(gè)Server都有一個(gè)response線程和選舉線程,我們先看一下每個(gè)線程是做一些什么事情
response線程
它主要功能是被動(dòng)的接受對方法的請求,并根據(jù)當(dāng)前自己的狀態(tài)作出相應(yīng)的回復(fù),每次回復(fù)都有自己的Id,以及xid,我們根據(jù)他的狀態(tài)來看一看他都回復(fù)了哪些內(nèi)容
LOOKING狀態(tài):
自己要推薦的Server相關(guān)信息(id,zxid)
LEADING狀態(tài)
myid,上一次推薦的Server的id
FLLOWING狀態(tài):
當(dāng)前Leader的id,以及上一次處理的事務(wù)ID(zxid)
選舉線程
選舉線程由當(dāng)前Server發(fā)起選舉的線程擔(dān)任,他主要的功能對投票結(jié)果進(jìn)行統(tǒng)計(jì),并選出推薦的Server。選舉線程首先向所有Server發(fā)起一次詢問(包括自己),被詢問方,根據(jù)自己當(dāng)前的狀態(tài)作相應(yīng)的回復(fù),選舉線程收到回復(fù)后,驗(yàn)證是否是自己發(fā)起的詢問(驗(yàn)證 xid是否一致),然后獲取對方的id(myid),并存儲到當(dāng)前詢問對象列表中,最后獲取對方提議的leader相關(guān)信息(id,zxid),并將這些 信息存儲到當(dāng)次選舉的投票記錄表中,當(dāng)向所有Server都詢問完以后,對統(tǒng)計(jì)結(jié)果進(jìn)行篩選并進(jìn)行統(tǒng)計(jì),計(jì)算出當(dāng)次詢問后獲勝的是哪一個(gè) Server,并將當(dāng)前zxid最大的Server設(shè)置為當(dāng)前Server要推薦的Server(有可能是自己,也有可以是其它的Server,根據(jù)投票 結(jié)果而定,但是每一個(gè)Server在第一次投票時(shí)都會投自己),如果此時(shí)獲勝的Server獲得n/2 + 1的Server票數(shù), 設(shè)置當(dāng)前推薦的leader為獲勝的Server,將根據(jù)獲勝的Server相關(guān)信息設(shè)置自己的狀態(tài)。每一個(gè)Server都重復(fù)以上流程,直到選出 leader
了解每個(gè)線程的功能以后,我們來看一看選舉過程
當(dāng)一個(gè)Server啟動(dòng)時(shí)它都會發(fā)起一次選舉,此時(shí)由選舉線程發(fā)起相關(guān)流程,那么每個(gè)Server都會獲得當(dāng)前zxid最大的哪個(gè)Server是誰,如果當(dāng)次最大的Server沒有獲得n/2+1個(gè)票數(shù),那么下一次投票時(shí),他將向zxid最大的Server投票,重復(fù)以上流程,最后一定能選舉出一個(gè)Leader
只要保證n/2+1個(gè)Server存活就沒有任何問題,如果少于n/2+1個(gè)Server存活就沒辦法選出Leader
當(dāng)選舉出Leader以后,此時(shí)每個(gè)Server應(yīng)該是什么狀態(tài)(FLLOWING)都已經(jīng)確定,此時(shí)由于Leader已經(jīng)死亡我們就不管它,其它的Fllower按正常的流程繼續(xù)下去,當(dāng)完成這個(gè)流程以后,所有的Fllower都會向Leader發(fā)送Ping消息,如果無法ping通,就改變自己的狀態(tài)為(FLLOWING ==> LOOKING),發(fā)起新的一輪選舉
這個(gè)過程的處理跟選舉過程中Leader死亡處理方式一樣,這里就不再描述
第二種實(shí)現(xiàn): FastLeaderElection
fastLeaderElection是標(biāo)準(zhǔn)的fast paxos的實(shí)現(xiàn),它首先向所有Server提議自己要成為leader,當(dāng)其它Server收到提議以后,解決epoch和zxid的沖突,并接受對方的提議,然后向?qū)Ψ桨l(fā)送接受提議完成的消息
數(shù)據(jù)結(jié)構(gòu)
本地消息結(jié)構(gòu):
static public class Notification {
long leader; //所推薦的Server id
long zxid; //所推薦的Server的zxid(zookeeper transtion id)
long epoch; //描述leader是否變化(每一個(gè)Server啟動(dòng)時(shí)都有一個(gè)logicalclock,初始值為0)
QuorumPeer.ServerState state; //發(fā)送者當(dāng)前的狀態(tài)
InetSocketAddress addr; //發(fā)送者的ip地址
}
網(wǎng)絡(luò)消息結(jié)構(gòu):
static public class ToSend {
int type; //消息類型
long leader; //Server id
long zxid; //Server的zxid
long epoch; //Server的epoch
QuorumPeer.ServerState state; //Server的state
long tag; //消息編號
InetSocketAddress addr;
}
Server具體的實(shí)現(xiàn)
每個(gè)Server都一個(gè)接收線程池(3個(gè)線程)和一個(gè)發(fā)送線程池 (3個(gè)線程),在沒有發(fā)起選舉時(shí),這兩個(gè)線程池處于阻塞狀態(tài),直到有消息到來時(shí)才解除阻塞并處理消息,同時(shí)每個(gè)Server都有一個(gè)選舉線程(可以發(fā)起 選舉的線程擔(dān)任);我們先看一下每個(gè)線程所做的事情,如下:
被動(dòng)接收消息端(接收線程池)的處理:
notification: 首先檢測當(dāng)前Server上所被推薦的zxid,epoch是否合法(currentServer.epoch <= currentMsg.epoch && (currentMsg.zxid > currentServer.zxid || (currentMsg.zxid == currentServer.zxid && currentMsg.id > currentServer.id))) 如果不合法就用消息中的zxid,epoch,id更新當(dāng)前Server所被推薦的值,此時(shí)將收到的消息轉(zhuǎn)換成Notification消息放入接收隊(duì)列中,將向?qū)Ψ桨l(fā)送ack消息
ack: 將消息編號放入ack隊(duì)列中,檢測對方的狀態(tài)是否是LOOKING狀態(tài),如果不是說明此時(shí)已經(jīng)有Leader已經(jīng)被選出來,將接收到的消息轉(zhuǎn)發(fā)成Notification消息放入接收對隊(duì)列
主動(dòng)發(fā)送消息端(發(fā)送線程池)的處理:
notification: 將要發(fā)送的消息由Notification消息轉(zhuǎn)換成ToSend消息,然后發(fā)送對方,并等待對方的回復(fù),如果在等待結(jié)束沒有收到對方法回復(fù),重做三次,如果重做次還是沒有收到對方的回復(fù)時(shí)檢測當(dāng)前的選舉(epoch)是否已經(jīng)改變,如果沒有改變,將消息再次放入發(fā)送隊(duì)列中,一直重復(fù)直到有Leader選出或者收到對方回復(fù)為止
ack: 主要將自己相關(guān)信息發(fā)送給對方
主動(dòng)發(fā)起選舉端(選舉線程)的處理:
首先自己的epoch 加1,然后生成notification消息,并將消息放入發(fā)送隊(duì)列中,系統(tǒng)中配置有幾個(gè)Server就生成幾條消息,保證每個(gè)Server都能收到此消息,如果當(dāng)前Server的狀態(tài)是LOOKING就一直循環(huán)檢查接收隊(duì)列是否有消息,如果有消息,根據(jù)消息中對方的狀態(tài)進(jìn)行相應(yīng)的處理。
LOOKING狀態(tài):
首先檢測消息中epoch是否合法,是否比當(dāng)前Server的大,如果比較當(dāng)前Server的epoch大時(shí),更新epoch,檢測是消息中的zxid,id是否比當(dāng)前推薦的Server大,如果是更新相關(guān)值,并新生成notification消息放入發(fā)關(guān)隊(duì)列,清空投票統(tǒng)計(jì)表; 如果消息小的epoch則什么也不做; 如果相同檢測消息中zxid,id是否合法,如果消息中的zxid,id大,那么更新當(dāng)前Server相關(guān)信息,并新生成notification消息放入發(fā)送隊(duì)列,將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中,并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)
LEADING狀態(tài):
將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中(這里的統(tǒng)計(jì)表是獨(dú)立的),并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)
FOLLOWING狀態(tài):
將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中(這里的統(tǒng)計(jì)表是獨(dú)立的),并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)
了解每個(gè)線程的功能以后,我們來看一看選舉過程,選舉過程跟第一程一樣
當(dāng)一個(gè)Server啟動(dòng)時(shí)它都會發(fā)起一次選舉,此時(shí)由選舉線程發(fā)起相關(guān)流程,通過將自己的zxid和epoch告訴其它Server,最后每個(gè)Server都會得zxid值最大的哪個(gè)Server的相關(guān)信息,并且在下一次投票時(shí)就投zxid值最大的哪個(gè)Server,重復(fù)以上流程,最后一定能選舉出一個(gè)Leader
只要保證n/2+1個(gè)Server存活就沒有任何問題,如果少于n/2+1個(gè)Server存活就沒辦法選出Leader
當(dāng)選舉出Leader以后,此時(shí)每個(gè)Server應(yīng)該是什么狀態(tài) (FLLOWING)都已經(jīng)確定,此時(shí)由于Leader已經(jīng)死亡我們就不管它,其它的Fllower按正常的流程繼續(xù)下去,當(dāng)完成這個(gè)流程以后,所有的 Fllower都會向Leader發(fā)送Ping消息,如果無法ping通,就改變自己的狀態(tài)為(FLLOWING ==> LOOKING),發(fā)起新的一輪選舉
這個(gè)過程的處理跟選舉過 程中Leader死亡處理方式一樣,這里就不再描述