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