https://zookeeper.apache.org/
树形结构,类似linux的文件目录
每一个节点都是znode,里面可以包含数据,也可以有子节点
节点分为永久节点和临时节点(session失效,也就是客户端断开后,临时节点消失)
每个znode都有版本号,每当数据变化,版本号会累加(乐观锁)
删除或修改节点,当版本号不匹配(版本号已过时),则会报错
每个节点存储的数据不宜过大,几K即可
节点可以设置权限,来限制用户的访问
Zookeeper保证读和写都是原子操作,且每次读写操作都是对数据的完整读取或写入
持久节点:创建时进行设置,不允许再改变类型;只能显示删除,否则一直存在
临时节点:依赖于客户端回话,断开连接自动删除,也可手动删除;可以利用临时节点方便的进行集群管理,比如每一个临时节点代表一个服务实例,服务宕机时临时节点被删除,维护服务发现的功能。
顺序节点:持久节点和临时节点都可以是顺序的或非顺序的,如果创建一个持久或临时的顺序节点,会生成一个10位的分布式唯一ID
dataVersion 数据版本
cversion 子节点版本
aclVersion 权限控制版本
···
启动
./bin/zkServer.sh start
停止
./bin/zkServer.sh stop
重启
./bin/zkServer.sh restart
连接到服务器
./bin/zkCli.sh -server 127.0.0.1:2181
连接后输入help可查看命令
查看指定节点目录下节点内容
ls path
查看节点状态
stat path
创建节点
-s 顺序节点 -e 临时节点
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
查看节点数据
get path
修改节点数据
set [-s] [-v version] path data
删除
delete [-v version] path
触发器、监督者
触发器,监听节点变化通知客户端
使用zookeeper自带的客户端实现是临时的用过一次后销毁,使用apache客户端的可以永久性
使用场景:统一资源配置
例如多个client把redis的client和端口号信息存到zookeeper上,如果没有watcher,只能不断轮询,比如几秒钟查一次,不可靠且开销大;使用watcher数据变化后会通知client
它使用权限位来允许/禁止对节点及其所有作用域的各种操作
ACL仅与特定的znode有关,与子节点无关;比如设置一个节点只能被某ip读取,但是其子节点不受影响
ACL:[scheme采用的权限机制:id用户:permissions权限组合字符串]
world
auth
digest
ip
super
权限字符串crdwa
create
read
delete
write
admin
区分开发/测试/运维环境,防止误操作
可以针对不同IP而产生具体的配置,更安全;代价是IP变化需要再配置
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.8.0version>
dependency>
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %p %c{2}: %m%n
package com.test.zookeeper;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class ZKConnect implements Watcher {
//连接ZK所在的客户端服务器ip
public static final String SERVER_PATH = "ip:2181";
// 设置超时时间
public static final Integer TIME_OUT = 5000;
// 重写Watcher接口方法:
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("收到了通知"+watchedEvent);}
public static void main(String[] args) throws IOException, InterruptedException {
// 连接zk客户端:
ZooKeeper zk = new ZooKeeper(SERVER_PATH, TIME_OUT, new ZKConnect());
// 打印连接的状态:
System.out.println("正在连接ZK客户端");
System.out.println(zk.getState());
Thread.sleep(2000);
System.out.println(zk.getState()); //连接成功 CONNECTED
}
}
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
// 连接zk客户端:
ZooKeeper zk = new ZooKeeper(SERVER_PATH, TIME_OUT, new ZKConnect());
/**
* path:创建的路径
* data:数据
* acl:权限,开放
* createMode:永久、临时、顺序节点
*/
zk.create("/mytest", "mynode".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//修改节点 原节点 要修改的内容 版本号
// zk.setData("/mytest", "mytest2".getBytes(), 0);
// zk.delete("/mytest2", 2); //要返回删除信息可以自定义CallBack回掉函数
//获取节点
byte[] data = zk.getData("/mytest", null, null);
System.out.println(new String(data));
}
…
不支持连接超时后的自动重连
Watcher注册一次后会失效
不支持递归创建节点
依赖
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
用Curator操作ZK
package com.test.zookeeper.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
/**
* 用Curator操作ZK
*/
public class CuratorTests {
public static void main(String[] args) throws Exception {
String connectString = "82.156.22.230:2181";
RetryPolicy retry = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, retry);
client.start();
//监听器
client.getCuratorListenable().addListener((CuratorFramework c, CuratorEvent event)->{
switch (event.getType()) {
case WATCHED:
WatchedEvent watchedEvent = event.getWatchedEvent();
if (watchedEvent.getType() == Watcher.Event.EventType.NodeDataChanged) {
System.out.println(new String(c.getData().forPath(event.getPath())));
}
}
});
String path = "/my_test_node";
String data = "test_data";
String data2 = "test_data2";
client.create().withMode(CreateMode.PERSISTENT).forPath(path, data.getBytes()); //创建
byte[] bytes = client.getData().watched().forPath(path); //获取
System.out.println(new String(bytes));
client.setData().forPath(path, data2.getBytes()); //修改
client.delete().forPath(path); //删除
Thread.sleep(2000);
}
}