docker安装zookeeper服务端
首先安装单节点的服务端,如果安装多节点的服务端,需要为每个节点配置其他节点的地址.
docker run --privileged=true -d --name zookeeper --publish 2181:2181 -d zookeeper:latest
zookeeper命令
zookeeper客户端下载地址:https://github.com/apache/zookeeper
ZooKeeper客户端有C语言和Java两个版本。
ZooKeeper的命令在/usr/lib/zookeeper/bin文件夹下。
运行Java版本的客户端使用bash zkCli.sh -server IP:port ,运行C语言版本的使用./cli_mt IP:port,下面介绍Java版本的,C语言版差不多。
也可以使用zkCli.sh的非交互式模式来执行一次性操作,格式为:
zkCli.sh -server IP:PORT COMMAND
查看具体结点信息
root@ubuntu:/usr/lib/zookeeper/bin# bash zkServer.sh status
查看哪个结点被选作leader或者follower
root@ubuntu:/usr/lib/zookeeper/bin# echo stat|nc 127.0.0.1 2181
测试是否启动了该Server,若回复imok表示已经启动
root@ubuntu:/usr/lib/zookeeper/bin# echo ruok|nc 127.0.0.1 2181
ZooKeeper命令行类似于shell。
当启动 ZooKeeper 服务成功之后,输入下述命令,连接到 ZooKeeper 服务:
root@ubuntu:/usr/lib/zookeeper/bin# bash zkCli.sh -server 192.168.255.133:2181
输入help显示帮助信息:
help
ZooKeeper -server host:port cmd args
connect host:port
get path [watch]
ls path [watch]
set path data [version]
rmr path
delquota [-n|-b] path
quit
printwatches on|off
create [-s] [-e] path data acl
stat path [watch]
close
ls2 path [watch]
history
listquota path
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
setquota -n|-b val path
[zk: 192.168.255.133:2181(CONNECTED) 1]
连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息,并在屏幕输出“ Welcome to ZooKeeper ”等信息。
命令行工具的一些简单操作如下:
1 )使用 ls 命令来查看当前 ZooKeeper 中所包含的内容:
[zk: 202.115.36.251:2181(CONNECTED) 1] ls /
2 )创建一个新的 znode ,使用 create /zk myData 。这个命令创建了一个新的 znode 节点“ zk ”以及与它关联的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 2] create /zk "myData"
3 )我们运行 get 命令来确认 znode 是否包含我们所创建的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 3] get /zk
4 )下面我们通过 set 命令来对 zk 所关联的字符串进行设置:
[zk: 202.115.36.251:2181(CONNECTED) 4] set /zk "zsl"
5 )下面我们将刚才创建的 znode 删除:
[zk: 202.115.36.251:2181(CONNECTED) 5] delete /zk
当然,我们还可以创建有曾次的目录,比如使用create /zk/node1
在zk目录下创建新的目录node1
[zk: 192.168.255.133:2181(CONNECTED) 18] create /zk/node1 "node1"
[zk: 192.168.255.133:2181(CONNECTED) 19] ls /zk
[node1]
上传文件到zookeeper目录
/xx/zkcli.sh
-zkhost 192.168.66.100:2181, \ #指定连接的zookeeper服务器
192.168.66.100:2182, \
192.168.66.100:2183 \
-cmd upconfig \ #上传文件命令
-confname myconf \ #上传到zookeepr的目录 /configs/myconf
-confdir /xx/xx/xx/conf/ #上传的配置文件目录
向zookeeper写数据
向zookeeper的/my_zk_file.txt 文件中写入test字符串
/xx/zkcli.sh -zkhost 192.168.66.100:2181 -cmd put /my_zk_file.txt 'test'
外部配置zookeeper
如果其他软件需要借助zookeeper为集群管理,可以配置成如下方式、
zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181
如果使用的是zookeeper下的子节点,可以配置成
zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181/your-dir
python操作zookeeper
使用docker服务端和python客户端来测试部分功能
kazoo 的github:https://github.com/python-zk/kazoo
kazoo客户端官网:https://kazoo.readthedocs.io/en/latest/
安装kazoo python客户端
pip install kazoo
安装过程中会出现一些python依赖包未安装的情况,安装即可。
1、基本功能测试demo
import time
from kazoo.client import KazooClient
from kazoo.client import KazooState
def main():
zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()
# 状态变化会执行此句,可以理解为向kazoo注册了回调函数
@zk.add_listener
def my_listener(state):
if state == KazooState.LOST:
print("LOST")
elif state == KazooState.SUSPENDED:
print("SUSPENDED")
else:
print("Connected")
# 确定节点是否存在
if zk.exists("/my/favorite/node/a"):
print("/my/favorite/node/a is existed")
# 删除节点
zk.delete("/my/favorite/node/a")
# 删除多层节点
zk.delete("/my/favorite", recursive=True)
# 创建多层节点,不携带数据
zk.ensure_path("/my/favorite")
# 创建节点,有数据
zk.create("/my/favorite/node", b"1")
zk.create("/my/favorite/node/a", b"2")
# Reading Data
# 孩子节点有变化,就会执行此句,可以理解为向kazoo注册了回调函数
@zk.ChildrenWatch("/my/favorite/node")
def watch_children(children):
print("node change, Children are now: %s" % children)
# 节点数据有变化,就会执行此句,可以理解为向kazoo注册了回调函数
@zk.DataWatch("/my/favorite/node")
def watch_node(data, stat):
print("data change, Version: %s, data: %s" % (stat.version, data.decode("utf-8")))
# 读取节点信息和数据
data, stat = zk.get("/my/favorite/node")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))
# 获取子节点
children = zk.get_children("/my/favorite/node")
print("There are %s children with names %s" % (len(children), children))
# 更新数据
zk.set("/my/favorite", b"some data")
# 事务
transaction = zk.transaction()
transaction.check('/my/favorite/node', version=-1)
transaction.create('/my/favorite/node/b', b"B")
results = transaction.commit()
print("Transaction results is %s" % results)
time.sleep(2)
zk.stop()
if __name__ == "__main__":
try:
main()
except Exception as ex:
print("Ocurred Exception: %s" % str(ex))
quit()
以上程序运行了基本kazoo接口命令,包括创建删除加watcher等操作,通过调试并对比zookeeper服务节点znode目录结构的变化,就可以理解具体的操作结果。
基于zookeeper实现分布式锁
解释: 左边的整个区域表示一个Zookeeper集群,locker是Zookeeper的一个持久节点,node_1、node_2、node_3是locker这个持久节点下面的临时顺序节点。client_1、client_2、client_n表示多个客户端,Service表示需要互斥访问的共享资源。
分布式锁获取思路
1.获取分布式锁的总体思路
在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。客户端调用createNode方法在locker下创建临时顺序节点,
然后调用getChildren(“locker”)来获取locker下面的所有子节点,注意此时不用设置任何Watcher。客户端获取到所有的子节点path之后,如果发现自己在之前创建的子节点序号最小,那么就认为该客户端获取到了锁。如果发现自己创建的节点并非locker所有子节点中最小的,说明自己还没有获取到锁,
此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。
之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locker子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。当前这个过程中还需要许多的逻辑判断。
python运行通过kazoo实现的分布式锁程序kazoo_lock.py
import logging
import os
import time
from kazoo.client import KazooClient
from kazoo.client import KazooState
from kazoo.recipe.lock import Lock
class ZooKeeperLock():
def __init__(self, hosts, id_str, lock_name, logger=None, timeout=1):
self.hosts = hosts
self.id_str = id_str
self.zk_client = None
self.timeout = timeout
self.logger = logger
self.name = lock_name
self.lock_handle = None
self.create_lock()
def create_lock(self):
try:
self.zk_client = KazooClient(
hosts=self.hosts,
logger=self.logger,
timeout=self.timeout)
self.zk_client.start(timeout=self.timeout)
except Exception as ex:
self.init_ret = False
self.err_str = "Create KazooClient failed! Exception: %s" % str(ex)
logging.error(self.err_str)
return
try:
lock_path = os.path.join("/", "locks", self.name)
print(lock_path)
self.lock_handle = Lock(self.zk_client, lock_path)
except Exception as ex:
self.init_ret = False
self.err_str = "Create lock failed! Exception: %s" % str(ex)
logging.error(self.err_str)
return
def destroy_lock(self):
# self.release()
if self.zk_client is not None:
self.zk_client.stop()
self.zk_client = None
# 获取锁
def acquire(self, blocking=True, timeout=None):
if self.lock_handle is None:
return None
try:
return self.lock_handle.acquire(blocking=blocking, timeout=timeout)
except Exception as ex:
self.err_str = "Acquire lock failed! Exception: %s" % str(ex)
logging.error(self.err_str)
return None
# 释放锁
def release(self):
if self.lock_handle is None:
return None
return self.lock_handle.release()
def __del__(self):
self.destroy_lock()
def main():
logger = logging.getLogger()
logger.setLevel(logging.INFO)
sh = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s -%(module)s:%(filename)s-L%(lineno)d-%(levelname)s: %(message)s')
sh.setFormatter(formatter)
logger.addHandler(sh)
zookeeper_hosts = "127.0.0.1:2181"
lock_name = "test"
lock = ZooKeeperLock(
zookeeper_hosts,
"myid is 1",
lock_name,
logger=logger)
# 阻塞操作,会等到获取到锁才会运行
ret = lock.acquire()
if not ret:
logging.info("Can't get lock! Ret: %s", ret)
return
logging.info("Get lock! Do something! Sleep 10 secs!")
for i in range(1, 11):
time.sleep(1)
print(str(i))
lock.release()
if __name__ == "__main__":
try:
main()
except Exception as ex:
print("Ocurred Exception: %s" % str(ex))
quit()
将该测试文件copy到多个服务器,同时运行,就可以看到分布式锁的效果了。
基于zookeeper实现分布式队列
Zookeeper可以处理两种类型的队列:
(1)同步队列
当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。例如一个班去旅游,看是否所有人都到齐了,到齐了就发车。例如有个大任务分解为多个子任务,要所有子任务都完成了才能进入到下一流程。
(2)先进先出队列
按照FIFO方式进行入队和出队
例如实现生产者和消费者模型
(1)同步队列
在zookeeper中先创建一个根目录 queue_sync做为队列,队列的成员监视/queue_sync/start节点,刚开始还没有这个节点,所以什么都不会做。成员入队操作就是在queue_sync下创建子节点 /queue_sync/x(i),然后计算子节点的总数,看是否和队列的目标数量相同。如果相同,创建/queue_sync/start节点,由于/queue_sync/start这个节点有了状态变化,zookeeper就会通知监视者(每个成员):队员已经到齐了,监视者得到通知后进行自己的后续流程
(2)先进先出队列
在zookeeper中先创建一个根目录 queue_fifo,做为队列。入队操作就是在queue_fifo下创建自增序的子节点,并把数据放入节点内。出队操作就是先找到queue_fifo下序号最下的那个节点,取出数据,然后删除此节点。