您好,登錄后才能下訂單哦!
zookeeper能夠同步同步各節(jié)點(diǎn)的znode數(shù)據(jù),client可以使用getChildren,getData,exists方法在znode tree路徑上設(shè)置watch,當(dāng)watch路徑上發(fā)生節(jié)點(diǎn)create、delete、update的時(shí)候,會(huì)通知到client。client可以得到通知后,再獲取數(shù)據(jù),執(zhí)行業(yè)務(wù)邏輯操作。
但是因?yàn)闆](méi)有消息接收后的確認(rèn)機(jī)制,這個(gè)通知機(jī)制是不可靠的,也就是說(shuō)znode的修改者并不知道是否所有的client都被通知到了,或者說(shuō)client也不知道自己是否錯(cuò)過(guò)了哪些通知消息。這種現(xiàn)象可能由網(wǎng)絡(luò)原因引起,也可能是client剛觸發(fā)了watch事件,還沒(méi)有來(lái)得及重新設(shè)置watch,下個(gè)事件就發(fā)生了(zookeeper只提供了一次性watch)。
在筆者的使用場(chǎng)景中,這是有問(wèn)題的。在我們分布式配置管理的場(chǎng)景中,有3份配置副本,管理節(jié)點(diǎn)本地?cái)?shù)據(jù)庫(kù)中有一份,zookeeper中保存了一份,使用配置的軟件引擎進(jìn)程中保存了一份。當(dāng)下發(fā)配置時(shí),是需要全部軟件引擎都生效最新的配置,而如果有某個(gè)引擎錯(cuò)過(guò)了通知,那么就會(huì)漏掉某個(gè)配置,導(dǎo)致問(wèn)題,而到底誰(shuí)沒(méi)有成功生效,這些節(jié)點(diǎn)都不知道,甚至錯(cuò)過(guò)通知的節(jié)點(diǎn)自身也不知道。
因此我提出一種設(shè)計(jì),至少讓錯(cuò)過(guò)通知的節(jié)點(diǎn)自身知道錯(cuò)過(guò)了消息,并采取主動(dòng)同步配置的方式,來(lái)補(bǔ)救。這樣能夠保證,配置下發(fā)后,至少一段時(shí)間后所有軟件引擎的都使用了最新的配置。
這利用了zookeeper自身的幾個(gè)特性:
1)zookeeper維護(hù)一個(gè)全局的操作id,zxid,每一個(gè)create,delete,update操作都會(huì)使該id加1。
2)zookeeper為每個(gè)路徑(znode)節(jié)點(diǎn)都保存了它的修改版本dataversion,和最新一次修改zxid——mzxid。
3)zookeeper保證每個(gè)client連接的session中,看到的通知順序與這些事件發(fā)生的先后順序是嚴(yán)格一致的。
我們來(lái)假設(shè)要在/group/policy下增加、刪除、修改配置,每個(gè)配置有1個(gè)節(jié)點(diǎn),配置數(shù)量可以很多、而且不固定。client要知道增加了什么配置,修改了什么,刪除了什么配置,因此設(shè)定了watch。其中A復(fù)雜修改這些配置,N1,N2,...Nm這些節(jié)點(diǎn)監(jiān)聽(tīng)通知,并更新軟件引擎的變量,使其生效配置。
首先A在/group/policy下做create、delete、update操作后,都要set 一次 /group/policy節(jié)點(diǎn)。這會(huì)導(dǎo)致/group/policy的dataversion加1。并且可以知道有幾次操作,dataversion就增加幾。
偽代碼如下:
doSomeOperion();
setData("/group/policy","")
Ni節(jié)點(diǎn)要對(duì)/group/policy下節(jié)點(diǎn)發(fā)生修改的事件進(jìn)行watch,還要對(duì)/group/policy節(jié)點(diǎn)自身的修改進(jìn)行watch。因此如果Ni沒(méi)有錯(cuò)過(guò)通知的話(huà),它將一次觸發(fā)兩個(gè)通知:1)配置變化通知;2)/group/policy數(shù)據(jù)更新通知。
Ni要在本地保存三個(gè)變量current_dataversion,current_zxid
在Ni client初始化時(shí):
current_dataversion=/group/policy的dataversion;
current_zxid=/group/policy的mzxid;
然后在watch到配置發(fā)生變化的回調(diào)函數(shù)中:
doSometing(); //生效具體配置
current_dataversion += 1; //期待/group/policy的下一個(gè)dataversion增加1
在watch到/group/policy的數(shù)據(jù)發(fā)生變化后回調(diào)函數(shù)中:
if current_dataversion == /group/policy的dataversion: //意味著沒(méi)有漏掉消息
current_zxid = /group/policy的mzxid
elif next_dataversion < /group/policy的dataversion: //一位置有配置變化的消息沒(méi)有收到
遍歷/group/policy子節(jié)點(diǎn)
if /group/policy/znodei的mzxid > current_zxid:
使用znodei中的配置。
刪除已經(jīng)不存在znode的配置項(xiàng);
同步完成;
next_dataversion = /group/policy的dataversion
current_zxid = /group/policy的mzxid
這樣就可以保證client能夠發(fā)現(xiàn)自己錯(cuò)過(guò)了消息,并發(fā)現(xiàn)哪些znode的修改被自己錯(cuò)過(guò)了。那么至少在下一次發(fā)生修改配置后,client能夠完全與當(dāng)前配置一致。
我們可以寫(xiě)一個(gè)場(chǎng)景驗(yàn)證下:
初始時(shí)/group/policy下為空,/group/policy的stat為(mzxid=2,dataversion=0)
current_dataversion=0; current_zxid=2 1)create /group/policy/n1(mzxid=3,dataversion=0) 收到通知 current_dataversion+=1 (等于1) 2)set /group/policy (mzxid=4,dataversion=1) 收到通知 curren_dataversion==/group/policy.dataversion,沒(méi)有漏掉通知 current_zxid=/group/policy.mzxid (等于4) 情形一) 3.1) create /group/policy/n2 (mzxid=5,dataversion=0) 沒(méi)有收到通知 current_dataversion不變(等于1) 4.1)set /group/policy (mzxid=6,dataversion=2) 收到通知 current_data < /group/policy.dataversion,得知漏掉了通知,并且知道漏掉1個(gè) 同步mzxid大于current_zxid(值為4)的節(jié)點(diǎn)(即n2節(jié)點(diǎn))配置; 刪除已經(jīng)不存在znode的配置; current_data = /group/policy.dataversion (等于2) current_zxid = /group/policy.zxid (等于5) 情形二) 3.2)create /group/policy/n2(mzxid=5,dataversion=0) 收到通知 current_dataversion+=1 (等于2) 4.2)set /group/policy (mzxid=6,dataversion=2) 沒(méi)有收到通知 current_zxid(=4)不變。漏掉該消息是沒(méi)有關(guān)系的,再次收到該消息時(shí),會(huì)更新current_zxid 情形三) 3.3)create /group/policy/n2(mzxid=5,dataversion=0) 沒(méi)收到通知 current_dataversion不變(等于1) 4.3)set /group/policy (mzxid=6,dataversion=2) 沒(méi)有收到通知 current_zxid(=4)不變。 5)create /group/policy/n3(mzxid=7,dataversion=0) 收到通知 current_dataversion+=1 (等于2) 6)set /group/policy (mzxid=8,dataversion=3) 收到通知 current_data < /group/policy.dataversion 得知漏掉了通知 同步mzxid大于current_zxid(值為4)的節(jié)點(diǎn)(即n2,n3節(jié)點(diǎn))配置; 刪除已經(jīng)不存在znode的配置; current_data = /group/policy.dataversion (等于3) current_zxid = /group/policy.zxid (等于8)
通過(guò)這種方式,可以讓client端知道自己錯(cuò)過(guò)了通知,至少在下次收到/group/policy節(jié)點(diǎn)更新通知時(shí),能夠重新同步配置。因此可以保證client之間遲早會(huì)變得同步。
更進(jìn)一步,可以額外再增加時(shí)鐘來(lái)觸發(fā)對(duì)/group/policy節(jié)點(diǎn)的檢查。這樣就可以保證一個(gè)時(shí)鐘間隔之后,client肯定是同步的。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。