溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

確保zookeeper一定收到通知消息的方法

發(fā)布時(shí)間:2020-07-14 21:44:13 來(lái)源:網(wǎng)絡(luò) 閱讀:1721 作者:myxxdyl 欄目:開(kāi)發(fā)技術(shù)

  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肯定是同步的。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI