功能
1.为客户端管理少量数据,key=value形式,key
是路径(节点),values
是字符串,
例子:
/a "helloworld"
2.为客户端监听指定节点的状态,就是上述的key
使用场景
1.多台服务器,共有相同配置文件,可以将配置文件放在zookeeper中,更改zookeeper中的配置文件,zookeeper就可以自动通知多台服务器
2.服务器的上线和下线感知.服务器启动时向zoo
注册ip
,port
等信息,客户端启动向zoo
请求服务器的信息,如果某台服务器挂掉,zoo
会发消息给客户端
安装zookeeper
删除安装包里的src
和docs
文件夹
1.修改配置文件
conf/zoo_sample.cfg
改为conf/zoo.cfg
# 修改
dataDir=/root/zkdata # 修改数据存储路径,要自己新建,生成文件myid,内容为对应服务器编号(1,2,3)
# 添加
server.1=ip1:2888:3888 # 最好是奇数台
server.2=ip2:2888:3888 # 端口3888是投票选leader,2888是投票后用来通信的
server.3=ip3:2888:3888
到ip1上新建目录/root/zkdata
,并新建文件myid
内容为1
到ip2上新建目录/root/zkdata
,并新建文件myid
内容为2
到ip3上新建目录/root/zkdata
,并新建文件myid
内容为3
配置文件修改完后,将安装包拷贝给ip2 和ip3
2.建立存储文件
到ip1上,新建数据目录/root/zkdata,生成一个文件myid,内容为1
到ip2上,新建数据目录/root/zkdata,生成一个文件myid,内容为2
到ip3上,新建数据目录/root/zkdata,生成一个文件myid,内容为3
3.启动zookeeper
bin/zkServer.sh start
会有QuorumPeerMain
进程启动
bin/zkServer.sh status
查看状态,会有mod
指明是leader还是flower
我们杀死任何一台机器的zookeeper
,就会重新生成新的leader
一键启动脚本
for host in ip1 ip2 ip3
do
echo "${host}:${1}ing...."
ssh $host "source /etc/profile;/root/usr/local/zookeeper/bin/zkServer.sh ${1}" # ssh非正常登陆,不会加载profile文件,所以要手动加载
done
命令行客户端
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
方法 | 描述 |
---|---|
ls | ls / 查看节点 |
get | get /zookeeper 获得节点数据 |
create | create /a "hellozoo" 创建节点(key) 和数据(value) |
rmr | rmr /a 删除节点 |
set | set /a "hizoo" 更改节点内容 |
watch | get /a watch 监听a 节点内容,只起到一次作用 |
ls /a watch
监听节点变化
例子:
- 数据一致性
[zk: localhost:2181(CONNECTED) 2] create /a "hellozoo"
Created /a
[zk: localhost:2181(CONNECTED) 3] get /a
"hellozoo"
cZxid = 0x100000002
ctime = Thu Nov 07 22:15:19 EST 2019
mZxid = 0x100000002
mtime = Thu Nov 07 22:15:19 EST 2019
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
[zk: localhost:2181(CONNECTED) 4] set /a "hellohaddoop"
cZxid = 0x100000002
ctime = Thu Nov 07 22:15:19 EST 2019
mZxid = 0x100000003
mtime = Thu Nov 07 22:15:50 EST 2019
pZxid = 0x100000002
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
dataVersion 每更改一次都会计数
dataLength 数据的长度
numChildren 子节点数量
2.监听
`ls /a watch` 监听节点变化,其他端口更改了节点,会受到消息,一次性
`get /a watch` 监听节点变化,其他端口更改了节点内容,会受到消息,一次性
node多种类型
create [-s] [-e] path data acl
1、PERSISTENT 持久的:创建者就算跟集群断开联系,该类节点也会持久存在与zk集群中
2、EPHEMERAL 短暂的:创建者一旦跟集群断开联系,zk就会将这个节点删除
3、SEQUENTIAL 带序号的:这类节点,zk会自动拼接上一个序号,而且序号是递增的
组合类型:
PERSISTENT :持久不带序号
EPHEMERAL :短暂不带序号
PERSISTENT 且 SEQUENTIAL :持久且带序号
EPHEMERAL 且 SEQUENTIAL :短暂且带序号
javaAPI
依赖:
zookeeper-3.4.6.jar
/lib/jline-xxx.jar
/lib/log4j-xxx.jar
/lib/netty-xxx.jar
/lib/slf4j-api-xxx.jar
/lib/slf4j-log4j12-xxx.jar
增删改查
package demo;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Before;
import org.junit.Test;
public class ZookeeperClientDemo {
ZooKeeper zk = null;
@Before
public void init() throws Exception{
// 构造zookeeper的客户端对象,第一个参数传入多个值,以防某个几点挂掉
zk = new ZooKeeper("ip1:2181,ip2:2181,ip3:2181", 2000, null);
}
@Test //增
public void testCreate() throws Exception{
// 要创建的节点路径 ;数据 ;访问权限 ;节点类型
String create = zk.create("/zoo", "hello zoo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(create);
zk.close();
}
@Test //改
public void testUpdate() throws Exception {
// 节点路径 ;数据 ;所要修改的版本,-1代表任何版本
zk.setData("/zoo", "hello".getBytes("UTF-8"), -1);
zk.close();
}
@Test //查
public void testGet() throws Exception {
// ;节点路径 ;是否要监听 ;所要获取的数据的版本,null表示最新版本
byte[] data = zk.getData("/zoo", false, null);
System.out.println(new String(data,"UTF-8"));
zk.close();
}
@Test //查
public void testListChildren() throws Exception {
// 节点路径 ;是否要监听
List<String> children = zk.getChildren("/zoo", false);
for (String child : children) {
System.out.println(child);
}
zk.close();
}
@Test //删
public void testRm() throws InterruptedException, KeeperException{
zk.delete("/zoo", -1);
zk.close();
}
}
监听
package demo;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Before;
import org.junit.Test;
public class ZookeeperWatchDemo {
ZooKeeper zk = null;
@Before
public void init() throws Exception {
// 构造一个连接zookeeper的客户端对象
zk = new ZooKeeper("ip1:2181,ip2:2181,ip3:2181", 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
//先判断是否是新建,如果是新建状态则不回调
if (event.getState() == KeeperState.SyncConnected && event.getType() == EventType.NodeDataChanged) {
System.out.println(event.getPath()); // 收到的事件所发生的节点路径
System.out.println(event.getType()); // 收到的事件的类型
System.out.println("内容更改了"); // 收到事件后,我们的处理逻辑
try {
zk.getData("/zoo", true, null);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
//判断子节点发生变化的回调
}else if(event.getState() == KeeperState.SyncConnected && event.getType() == EventType.NodeChildrenChanged){
System.out.println("子节点变化了..");
}
}
});
}
@Test
public void testGetWatch() throws Exception {
byte[] data = zk.getData("/zoo", true, null); // 监听节点数据变化
List<String> children = zk.getChildren("/zoo", true); //监听节点的子节点变化事件
System.out.println(new String(data, "UTF-8"));
Thread.sleep(Long.MAX_VALUE);//不能zk.close(),关闭后不能监听,也不能什么也没有,运行完会自动关闭,所以要sleep,如果有消息来回zk会再启动一个线程
}
}