Docker下使用ZooKeeper
在/usr/local/zookeeper目录下保存ZooKeeper数据与数据卷卷
cd /usr/local && mkdir zookeeper && cd zookeeper
mkdir data
开始部署
-
部署命令
docker run -d -e TZ="Asia/Shanghai" -p 2181:2181 -v $PWD/data:/data --name zookeeper --restart always zookeeper
-
命令详细说明
-e TZ="Asia/Shanghai" # 指定上海时区 -d # 表示在一直在后台运行容器 -p 2181:2181 # 对端口进行映射,将本地2181端口映射到容器内部的2181端口 --name # 设置创建的容器名称 -v # 将本地目录(文件)挂载到容器指定目录; --restart always #始终重新启动zookeeper
-
查看容器启动情况
docker ps
常规安装ZooKeeper
要启动 ZooKeeper,您需要一个配置文件。配置文件名为zoo.cfg。在conf/zoo.cfg中创建它:
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
-
tickTime :ZooKeeper 使用的基本时间单位,以毫秒为单位。 It is used to do heartbeats and the minimum session timeout will be twice the tickTime.(它用于执行heartbeats,最小会话超时将是 tickTime 的两倍。)
-
dataDir : the location to store the in-memory database snapshots and, unless specified otherwise, the transaction log of updates to the database.(存储内存数据库快照的位置,除非另有说明,否则存储更新数据库的事务日志。)
-
clientPort : the port to listen for client connections(侦听客户端连接的端口)
ZooKeeper服务端操作命令:
1.启动 ZooKeeper:
./bin/zkServer.sh start
2.关闭ZooKeeper:
./bin/zkServer.sh stop
3.查看ZooKeeper状态:
./bin/zkServer.sh status
ZooKeeper客户端操作命令:
ZooKeeper的目录层级关系与Linux的目录层级关系类似,根目录为/。不过在ZooKeeper中数据不一定要在文件里,在目录下可以有数据。
在ZooKeeper中一个目录被称为一个znode。
1.开启ZooKeeper客户端:
./bin/zkCli.sh -server ip:port
连接后,您应该会看到类似以下内容:
Connecting to localhost:2181
...
Welcome to ZooKeeper!
JLine support is enabled
[zkshell: 0]
2.关闭ZooKeeper客户端:
quit
3.在 shell 中,键入help
以获取可从客户端执行的命令列表
4.查看znode目录下的子节点的信息:
ls /节点path
查看详细信息:
ls -s /节点path
5.创建znode:
create /需要新建节点的path/新建节点名 (可选)需要添加在当前节点的数据
创建临时节点:
create -e ...
创建顺序节点:
create -s ...
6.删除znode(当前节点非空时无法删除):
delete /节点path
连同子节点一起删除:
deleteAll /节点path
7.在znode下新建数据:
set /节点path 需要添加的数据
8.获取znode下的数据:
get /节点path
使用Curator的javaAPI操作ZooKeeper:
Curator API 的常用操作:
- 建立连接
- 添加节点
- 删除节点
- 修改节点
- 查询节点
- Watch事件监听
- 分布式锁的实现
建立连接
使用CuratorFrameworkFactory建立连接有两种方式:
1.静态工程模式
/*
CuratorFrameworkFactory.newClient():
创建一个客户端
Params:
connectString – zk服务端端口
sessionTimeoutMs – 会话超时时间(单位为毫秒)
connectionTimeoutMs – 连接超时时间(单位为毫秒)
retryPolicy – 重试策略(RetryPolicy接口类型的对象)
Returns:client
ExponentialBackoffRetry():
创建一个重试策略,继承了RetryPolicy接口
Params:
baseSleepTimeMs – 重试等待时间(单位为毫秒)
maxRetries – 重试次数
*/
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.67.101:2181",new ExponentialBackoffRetry(3000,5));
client.start();
//退出连接
CloseableUtils.closeQuietly(client);
2.链式编程(参数与上面类似)
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181")
.sessionTimeoutMs(1000)
.connectionTimeoutMs(1000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("test")//(名称空间:将操作目录从根目录/替换为这里定义的目录)
.build();
client.start();
//退出连接
CloseableUtils.closeQuietly(client);
下面看下 CuratorFramework 提供的方法:
Curator基本操作
1.创建节点
/*
CreateMode.
PERSISTENT//znode 不会在客户端断开连接时自动删除。
PERSISTENT_SEQUENTIAL//znode 不会在客户端断开连接时自动删除,及其名称将附加一个单调递增的数字。
EPHEMERAL//znode 将在客户端断开连接时被删除。
EPHEMERAL_SEQUENTIAL//znode 将在客户端断开连接时被删除,其名称
将附加一个单调递增的数字。
CONTAINER//znode 将是一个容器节点。容器
节点是对配方有用的特殊用途节点,例如领导者、锁、
等。当删除容器的最后一个子容器时,容器变为
将来某个时间被服务器删除的候选人。
鉴于此属性,您应该准备好获得
{@link org.apache.zookeeper.KeeperException.NoNodeException}
在此容器节点内创建子节点时。
PERSISTENT_WITH_TTL//znode 不会在客户端断开连接时自动删除。
然而,如果 znode 在给定的 TTL 内没有被修改,它
一旦没有孩子,将被删除。
PERSISTENT_SEQUENTIAL_WITH_TTL//znode 不会在客户端断开连接时自动删除,
及其名称将附加一个单调递增的数字。
然而,如果 znode 在给定的 TTL 内没有被修改,它
一旦没有孩子,将被删除。
*/
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/app1","data".getBytes());
//forPath("节点path")
//forPath("节点path","数据".getBytes())
//withMode(CreateMode.节点类型)
//creatingParentsIfNeeded() 如果没有父节点则创建父节点(默认没有父节点无法创建)
2.查询节点
-
查询数据 get
byte[] data = client.getData().forPath("/app1");
-
查询子节点 ls
List<String> path = client.getChildren().forPath("/app1");
-
查询节点详细信息 ls -s
Stat status = new Stat(); client.getData().storingStatIn(status).forPath("/app1"); System.out.println(status);
3.修改节点
Stat status = new Stat();
client.getData().storingStatIn(status).forPath("/app1");//使用乐观锁防止多人同时修改一个数据
client.setData().withVersion(status.getVersion()).forPath("/app1","data2".getBytes());
4.删除节点
client.delete().guaranteed().deletingChildrenIfNeeded().forPath("app1");
//deletingChildrenIfNeeded() deleteAll,连同子节点一起删除
//guaranteed() 必须删除,添加后如果遇上连接断开等问题会一直尝试删除该节点直到成功执行
回调(删除时执行一个回调函数):
client.delete().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent curatorEvent) throws Exception {
//回调函数体内
}
}).forPath("app1");
Curator的事务
// 开启事务
CuratorTransaction transaction = client.inTransaction();
Collection<CuratorTransactionResult> results = transaction.create().forPath("/app1/1")
.and().setData().forPath("/app1", "data".getBytes())
.and().create().forPath("/app1/2", "data".getBytes())
.and().commit();
Curator的Watch事件监听
ZooKeeper提供了三种Watcher:
- NodeCache:只是监听某一个特定的节点
- PathChildrenCache:监听一个ZNode的所有子节点(只能向下监听一层,并且也监听不到指定节点自身)
- TreeCache:监听整个ZNode树上的节点(不是整个ZooKeeper树,是你当前指定的某个ZNode的树)
注册监听模板:
//创建Watcher对象
NodeCache nodeCache = new NodeCache(client,"/app1",true);//如果设置为true,设置缓存数据
//NodeCache,PathChildrenCache,TreeCache
//注册监听
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {//nodeChanged()为一个事件,PathChildrenCache,TreeCache
//中还有别的事件
//当事件发生时执行当前方法体的代码
}
});
//lambda表达式改造
/*nodeCache.getListenable().addListener(() -> {
//当事件发生时执行当前方法体的代码
//可以获取当前数据
byte[] data = nodeCache.getCurrentData().getData();
});*/
//开启监听
nodeCache.start(true);//如果设置为true,加载缓存数据
//创建Watcher对象
PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app1",true);
//注册监听
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println(event);
}
});
//开启监听
pathChildrenCache.start(true);//如果设置为true,加载缓存数据
事件类型:
Curator使用ZooKeeper分布式锁
curator中有五种锁方案:
- InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
- InterProcessMutex:分布式可重入排它锁
- InterProcessReadWriteLock:分布式读写锁
- InterProcessMultiLock:将多个锁作为单个实体管理的容器
- InterProcessSemaphoreV2:共享信号量
使用锁的代码:
InterProcessLock interProcessLock;
interProcessLock = new InterProcessMutex(client,"/lock");//设置锁的存放路径
interProcessLock.acquire(3, TimeUnit.SECONDS);//获取锁,并设置等待时间
//对数据的操作
interProcessLock.release();//释放锁