Kazoo是一个Python库,旨在轻松简单地使用Zookeeper。
ZooKeeper是一款分布式协调服务中间件,是Apache Hadoop的一个子项目,用于构建健壮的分布式系统,提供一组API实现通用的协作任务,包括选举主节点、管理组内成员关系、管理元数据等。
ZooKeeper关注协同数据(元数据)。比如,网络邮箱用户对自己邮箱内容感兴趣,对哪台服务器处理请求不关心。那么邮箱内容就是应用数据,邮箱到某一台服务器之间的映射关系就是协同数据(原数据)。
ZooKeeper不适合作海量数据存储。
运行ZooKeeper的机器需要安装JRE
ZooKeeper官方下载
建议下载ZooKeeper 3.4.14
data
和log
zoo.cfg
dataDir=C:\\zookeeper-3.4.14\\data
,添加dataLogDir=C:\\zookeeper-3.4.14\\log
zkServer.cmd
启动服务cmd下输入jps
命令可以查看Java进程的PID
ZooInspector
为ZooKeeper的可视化工具,选装
下载地址
解压后运行build文件夹中的zookeeper-dev-ZooInspector.jar
pip install kazoo
运行ZooKeeper启动服务
使用kazoo需要实例化一个KazooClient对象并建立连接
from kazoo.client import KazooClient
zk = KazooClient(hosts='127.0.0.1:2181') # 默认连接本地zookeeper服务,端口号为2181
zk.start()
KazooClient默认连接本地zookeeper服务,端口为2181
当ZooKeeper服务异常时,zk.start()会不断重连直到超时。
连接一旦建立,若发生间歇性连接丢失或ZooKeeper会话过期,KazooClient会不断重连。
中断连接使用stop命令
zk.stop()
只读连接
from kazoo.client import KazooClient
zk = KazooClient(hosts='127.0.0.1:2181', read_only=True)
zk.start()
ensure_path()
:递归创建节点路径,只能设置权限,不能添加数据。
create()
:创建节点,可添加数据和监听事件。需要父节点存在,不能递归创建。
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
zk.ensure_path("/my/favorite") # 确保路径存在
zk.create("/my/favorite/node", b"Hello World!") # 创建带数据的节点node
运行ZooInspector可看到已添加进一个新节点
!!!注意写上那个b!!!
exists()
:检查节点是否存在,存在则返回节点的ZnodeStat,不存在则返回None
get()
:获取节点数据和详细节点状态(ZnodeStat)
get_children()
:获取节点的所有子节点
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
if zk.exists("/my/favorite"): # 检查节点是否存在
data, stat = zk.get("/my/favorite/node")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8"))) # 输出节点版本和数据
children = zk.get_children("/my/favorite")
print("There are %s children with names %s" % (len(children), children)) # 列出子节点
Version: 0, data: Hello World!
There are 1 children with names ['node']
set()
:更新指定节点的数据
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
zk.set("/my/favorite", b"some data")
data, stat = zk.get("/my/favorite/")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))
Version: 1, data: some data
delete()
:删除指定节点
可选择递归删除节点及其子节点,默认不递归
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
zk.delete("/my/favorite", recursive=True) # 递归删除
if zk.exists("/my/favorite/node") == None:
print('不存在node节点')
if zk.exists("/my/favorite") == None:
print('不存在favorite节点')
if zk.exists("/my") != None:
print('存在my节点')
不存在node节点
不存在favorite节点
存在my节点
Kazoo会话状态有三种:CONNECTED
、 SUSPENDED
、LOST
刚创建KazooClient实例时,状态为LOST
一旦连接成功,状态切换为CONNECTED
发生网络闪断、服务异常等导致客户端与服务端出现断开,状态切换为SUSPENDED,此时KazooClient会不断尝试重连
有效状态转换
Kazoo可以在节点上设置监听器,在节点或子节点变化时触发。
Kazoo有两种方式设置监听器,第一种是Zookeeper默认支持的一次性监听事件,当节点被改或子节点被删时触发
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
zk.ensure_path("/my/favorite") # 确保路径存在
zk.create("/my/favorite/node", b"Hello World!") # 子节点node
def watch_data(event):
print(event)
node = zk.get("/my/favorite", watch=watch_data) # 当节点被改时,调用watch_data
children = zk.get_children("/my/favorite/", watch=watch_data) # 当子节点被删,调用watch_data
zk.set("/my/favorite", b"some data") # 修改节点触发
print(1)
zk.set("/my/favorite/node", b"Hi!") # 修改子节点不触发
print(2)
zk.delete("/my/favorite/node") # 删除子节点触发
print(3)
zk.delete("/my/favorite") # 删除节点不触发
print(4)
WatchedEvent(type='CHANGED', state='CONNECTED', path='/my/favorite')
1
2
WatchedEvent(type='CHILD', state='CONNECTED', path='/my/favorite')
3
4
★第二种设置监听器的方式是利用Python修饰器,监听数据和子节点的修改
from kazoo.client import KazooClient
zk = KazooClient()
zk.start()
zk.ensure_path("/my/favorite") # 确保路径存在
@zk.ChildrenWatch("/my/favorite")
def watch_children(children):
"""监听子节点"""
print("Children are now: %s" % children)
@zk.DataWatch("/my/favorite")
def watch_node(data, stat, event=None):
"""监听节点数据"""
if event==None:
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))
else:
print(event)
print(0)
zk.create("/my/favorite/node") # 创建子节点,触发ChildrenWatch和DataWatch
print(1)
zk.set("/my/favorite/node", b"Hello World!") # 修改子节点,触发ChildrenWatch
print(2)
zk.delete("/my/favorite/node") # 删除子节点,触发ChildrenWatch
print(3)
zk.set("/my/favorite", b"some data") # 修改节点,触发DataWatch
print(4)
zk.delete("/my/favorite") # 删除节点,触发DataWatch
print(5)
Children are now: []
Version: 0, data:
0
1
Children are now: ['node']
2
3
Children are now: []
4
WatchedEvent(type='CHANGED', state='CONNECTED', path='/my/favorite')
5
WatchedEvent(type='DELETED', state='CONNECTED', path='/my/favorite')
from kazoo.client import KazooState
def my_listener(state):
if state == KazooState.LOST:
# 会话丢失
print("LOST")
elif state == KazooState.SUSPENDED:
# 会话断开
print("SUSPENDED")
else:
# 会话连接
print("CONNECTED")
zk.add_listener(my_listener)
使用kazoo.recipe.lock.Lock
和创建临时节点时,建议添加状态监听来处理连接中断和会话丢失
from kazoo.client import KazooState
from kazoo.client import KeeperState
@zk.add_listener
def watch_for_ro(state):
if state == KazooState.CONNECTED:
if zk.client_state == KeeperState.CONNECTED_RO:
print("Read only mode!")
else:
print("Read/Write mode!")