什么是Zookeeper?
Zookeeper是一個(gè)分布式服務(wù)管理軟件,它可以幫助我們完成自動(dòng)的分布式服務(wù)集成以及服務(wù)發(fā)現(xiàn)
為什么要用Zookeeper?
當(dāng)我們使用分布式服務(wù)時(shí),因此所帶來的分布式管理變得尤為重要,所以我們需要這樣一個(gè)開源軟件來協(xié)助我們管理所有分布式的服務(wù),提高我們服務(wù)的穩(wěn)定性和可擴(kuò)展性
安裝
Zookeeper是一款托管在Apache基金會(huì)的開源軟件
下載地址:https://www.apache.org/dyn/closer.cgi/zookeeper/
下載后解壓然后需要我們創(chuàng)建一個(gè)存放Zookeeper數(shù)據(jù)的文件夾
在conf/文件夾中我們可以看到一個(gè)sample_conf.cfg,可以參照這個(gè)配置文件來做自己的配置
啟動(dòng)Zookeeper命令:zkServer start
停止Zookeeper命令:zkServer stop
Zookeeper可以將我們的服務(wù)以節(jié)點(diǎn)Znode的形式管理,類似于我們的文件管理,每個(gè)節(jié)點(diǎn)可以保存1MB的內(nèi)容
持久節(jié)點(diǎn):在客戶端創(chuàng)建連接并且創(chuàng)建節(jié)點(diǎn)后如果關(guān)閉了連接,不會(huì)刪除Zookeeper中所保存的這個(gè)節(jié)點(diǎn)的信息,對(duì)于別的節(jié)點(diǎn)仍舊可見
臨時(shí)節(jié)點(diǎn):在客戶端創(chuàng)建連接并創(chuàng)建節(jié)點(diǎn)后,我們?nèi)绻@個(gè)時(shí)候斷開了客戶端的連接,那么之前在連接中所創(chuàng)建的節(jié)點(diǎn)以及所保存的數(shù)據(jù)都將會(huì)被清除
順序節(jié)點(diǎn):在創(chuàng)建節(jié)點(diǎn)的時(shí)候?qū)τ谀硞€(gè)服務(wù)來說,其他服務(wù)是不可見的,所以我們?cè)趧?chuàng)建節(jié)點(diǎn)的時(shí)候就很有可能重名,這個(gè)時(shí)候我們需要聲明這個(gè)節(jié)點(diǎn)是順序節(jié)點(diǎn),Zookeeper這個(gè)時(shí)候就會(huì)自動(dòng)幫我們創(chuàng)建好節(jié)點(diǎn)的后綴例如0000000001這樣十位數(shù)的后綴名
監(jiān)視:客戶端與Zookeeper之間一直會(huì)有心跳連接保持,這個(gè)時(shí)候如果說其中有某一臺(tái)服務(wù)斷開了連接,這個(gè)時(shí)候我們可以通過設(shè)置連接的監(jiān)視來監(jiān)聽這一變化,從而做出一些回調(diào)響應(yīng)
客戶端選擇kazoo 我們可以使用終端來測(cè)試kazoo的基本使用,因?yàn)樵趐ycharm中當(dāng)程序運(yùn)行完畢后會(huì)關(guān)閉標(biāo)準(zhǔn)輸出,我們就感受不到Zookeeper監(jiān)視特性給我們帶來的效果
from kazoo.client import KazooClient
import json
# 初始化客戶端
zk = KazooClient(hosts="127.0.0.1:2181")
# 啟動(dòng)Zookeeper客戶端
zk.start()
# 創(chuàng)建節(jié)點(diǎn)
zk.ensure_path("/rpc")
addr1 = json.dumps({
"host": "127.0.0.1",
"port": 8001
})
addr2 = json.dumps({
"host": "127.0.0.1",
"port": 8002
})
# 創(chuàng)建節(jié)點(diǎn)并在節(jié)點(diǎn)保存數(shù)據(jù) ephemeral表示是否是臨時(shí)節(jié)點(diǎn), 是否是順序節(jié)點(diǎn)
zk.create("/rpc/server", addr1.encode(), ephemeral=True, sequence=True)
zk.create("/rpc/server", addr2.encode(), ephemeral=True, sequence=True)
# 獲取子節(jié)點(diǎn)列表
children = zk.get_children("/rpc")
# 獲取某個(gè)節(jié)點(diǎn)中保存的數(shù)據(jù)和狀態(tài)
data, state = zk.get("/rpc/" + children[0])
print("某個(gè)節(jié)點(diǎn)的data:", data)
def func(event):
print(event)
print(event.__dict__)
print("節(jié)點(diǎn)數(shù)據(jù)發(fā)生變化")
print(zk.get_children("/rpc"))
# 監(jiān)視在獲取數(shù)據(jù)的時(shí)候set
n_children = zk.get_children("/rpc", watch=func)
某個(gè)節(jié)點(diǎn)的data: b'{"host": "127.0.0.1", "port": 8002}'
這個(gè)時(shí)候已經(jīng)創(chuàng)建了/rpc 父節(jié)點(diǎn)以及兩個(gè)/rpc/server的子節(jié)點(diǎn)
我們?cè)谶@個(gè)時(shí)候開啟另一個(gè)客戶端連接并且在/rpc節(jié)點(diǎn)下在創(chuàng)建一個(gè)節(jié)點(diǎn),這個(gè)時(shí)候就會(huì)觸發(fā)watch的效果
from kazoo.client import KazooClient
zk = KazooClient(hosts="127.0.0.1:2181")
zk.start()
zk.ensure_path("/rpc")
zk.create("/rpc/server", b"bwisgood", ephemeral=True, sequence=True)
輸出: WatchedEvent(type='CHILD', state='CONNECTED', path='/rpc')
{}
節(jié)點(diǎn)數(shù)據(jù)發(fā)生變化 ['server0000000004', 'server0000000003', 'server0000000005']
注意這里的watch方法需要在獲取子節(jié)點(diǎn)數(shù)據(jù)的時(shí)候設(shè)置,并且每設(shè)置一次就只會(huì)生效一次,如果需要固定的回調(diào)則需要在代碼中獲取子節(jié)點(diǎn)的時(shí)候固定watch參數(shù)