一、ZooKeeper 是什么
ZooKeeper是一种开源的为分布式应用所设计的协作服务,用于维护配置信息,命名,提供分布式同步和提供组服务。所有这些类型的服务都以分布式应用程序的某种形式使用。Zookeeper由java语言编写,支持Java和C两种编程语言。
二、ZooKeeper 提供了什么?
zookeeper提供了文件系统以及通知机制。
三、Zookeeper 文件系统
Zookeeper维护一个类似于文件系统的数据结构:
与文件系统相同的是其命名空间由斜线分隔开的路径名序列所组成,每一个节点都通过路径识别;
与文件系统不同的是每个节点中可以携带数据(包括数据,数据长度,创建时间,修改时间等),即每一个节点既可以当作文件夹来看,也可以当作文件来看。
通知机制
客户端注册监听他关心的目录节点,当节点目录发生变化时,zookeeper会通知客户端。
四、znode
Zookeeper的节点称为Znode,znode的特点如下:
1.每个节点都可以存储数据(kb级别),以及存储状态信息(时间戳,顺序号,ACL,版本号),可进行的操作(增删改查)。当节点的数据发生变化,其版本号将增加;
2.znode具原子性,其读写分离。读操作时读取所有znode数据,写操作将修改所有的数据;
3.每一个节点都具有ACL(Access Control List 访问控制列表),其规定用户对节点的操作权限。
znode的类型:
java源码在CreateMode枚举类中提供一下四种类型:
/**
* The znode will not be automatically deleted upon client's disconnect.
*/
PERSISTENT (0, false, false),
/**
* The znode will not be automatically deleted upon client's disconnect,
* and its name will be appended with a monotonically increasing number.
*/
PERSISTENT_SEQUENTIAL (2, false, true),
/**
* The znode will be deleted upon the client's disconnect.
*/
EPHEMERAL (1, true, false),
/**
* The znode will be deleted upon the client's disconnect, and its name
* will be appended with a monotonically increasing number.
*/
EPHEMERAL_SEQUENTIAL (3, true, true);
1. PERSISTENT:永久节点,即使客户端连接断开,节点依旧存在,使用delete才能删除,默认创建的是该类型的节点;
2. PERSISTENT_SEQUENTIAL:其具备PERSISTENT节点的特点,同时在创建时,它的路径末尾会附加一个单调递增的计数器;
3. EPHEMERAL:临时节点,客户端断开,临时节点就会失效,该节点下不能有子节点;
4. EPHEMERAL_SEQUENTIAL:其具备EPHEMERAL节点的特点,同时在创建时,它的路径末尾会附加一个单调递增的计数器;
拓展:
1. 临时节点
Zookeeper的客户端和服务器端通信采用的是长连接的方式,每个客户端和服务器端的通信通过保持心跳来连接,这个连接状态称为session。只要创建znode的会话处于活动状态,就会存在这些znode。客户端session失效,对应的临时节点便会失效,znode将被删除。由于这种行为,短暂的znodes不允许有子节点。由此可见,临时节点的生命周期是由session决定的。
一个临时znode,在以下两种情况下将会被删除:
1.当创建该znode的客户端的会话因超时或主动关闭而中止时。
2.当某个客户端(不一定是创建者)主动删除该节点时。
2.序列节点 - 唯一命名
创建序列节点时,ZooKeeper在节点路径末尾附加一个单调递增的计数器。此计数器对于父znode是唯一的。计数器的格式为%010d - 即10位数字,0(零)填充(计数器以这种方式格式化以简化排序)。注意:用于存储下一个序列号的计数器是由父节点维护的signed int(4字节),当递增超过2147483647时计数器将溢出(导致名称 ”-2147483648" )。
如下图: 我创建一个/aa的序列节点,zookeeper会自动在后面添加一个十位数的序列号。
五、Zookeeper安装(单机模式)
在zookeeper官网上下载安装包,我下载的是zookeeper-3.5.0-alpha.tar.gz,下载完成之后,使用Xftp或其他工具将文件上传到服务器,然后使用Xshell进行操作。
5.1 首先解压安装包
tar -zxvf zookeeper-3.5.0-alpha.tar.gz
5.2 解压后切换(cd)到conf目录下,然后在该目录下创建一个zoo_sample.cfg文件
tickTime=2000 ZooKeeper使用的基本时间单位(以毫秒为单位)。它用于做心跳,最小会话超时将是tickTime的两倍
dataDir=/usr/zk35/data 文件路径根据自己的进行修改 数据文件
clientPort=2181 客户端端口
5.3 创建好配置文件后,可以开始启动ZooKeeper:
bin/zkServer.sh start
bin/zkServer.sh start-foreground 启动前台
bin/zkServer.sh status 查看状态
5.4 可使用下列语句停止服务
bin/zkServer.sh stop
5.5 启动客户端,连接到Zookeeper
bin/zkCli.sh
5.6 连接成功后,会出现下面的内容
Connecting to localhost:2181
此处省略
watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@22927a81
Welcome to ZooKeeper!
2019-07-18 09:10:30,690 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1093] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2019-07-18 09:10:30,854 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@963] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-07-18 09:10:30,905 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1346] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x16c02a0f0950000, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
5.7 连接成功后,便可以在客户端进行操作了;
六、Zookeeper的简单操作
可使用help指令查看命令及其所需参数
[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
addauth scheme auth
close
config [-c] [-w] [-s]
connect host:port
create [-s] [-e] path [data] [acl]
delete [-v version] path
deleteall path
delquota [-n|-b] path
get [-s] [-w] path
getAcl [-s] path
history
listquota path
ls [-s] [-w] path
ls2 path [watch]
printwatches on|off
quit
reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]
redo cmdno
removewatches path [-c|-d|-a] [-l]
rmr path
set [-s] [-v version] path data
setAcl [-s] [-v version] path acl
setquota -n|-b val path
stat [-w] path
sync path
1. 创建Znode
语法:create /path /data ex: create /FirstZnode hello (3.5版本及以后) 默认创建的是持久的
创建顺序节点:create -s /path /data 保证Znode路径唯一
创建临时节点:create -e /path /data 前面已经说过,当会话过期或者是客户端连接断开后临时节点将被自动删除。
2.获取数据
语法:get /path ex: get /FirstZnode 如果访问顺序节点,必须输入完整路径:get /FirstZnode000000001
3.监视Znode
只能在get命令中设置
语法:get /path -w ex get /FirstZnode -w 开启监视后,可使用set对该znode进行修改,Server端会产生信息。
4.设置数据
语法: set /path /data
完成后,可使用get CLI命令检查数据
5.创建znode的子节点
语法:create /parent_path/subnode_path /data ex create /FirstZnode/subnode hello
6.查看znode下的子节点
语法:ls /path
7.检查状态
状态描述指定znode的元数据,包含时间戳,版本号,ACL,数据长度和子node等细项。
语法:stat /path
8.移除/删除znode
语法:rmr/delete/deleteall /path
区别:rmr/deleteall /path 可用于移除指定的znode并递归删除其下所有子节点
delete /path 只适用于没有子节点的znode
七、Java实现
在使用Zookeeper服务之前,我们必须先创建Zookeeper的实例(就像当时连接数据库一样,需要写一个JDBC工具类)。在org.apache.zookeeper中包含了Zookeeper类,我们在进行客户端操作时,会经常使用该类。如果Zookeeper和客户端建立连接,Zookeeper就会分配给该连接会话一个id值,并且客户端会顶起发送心跳给服务器来维持会话的连接。在该连接的基础上,我们就可以通过连接Zookeeper的客户端来调用API进行操作。
1. ZookeeperConnection类,用于连接Zookeeper
package com.yc.zktest1;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
public class ZookeeperConnection {
private static ZooKeeper zk;
final CountDownLatch connectSignal = new CountDownLatch(1);
public ZooKeeper connect(String host) throws IOException, InterruptedException {
zk = new ZooKeeper(host, 5000, new Watcher() {
// 回调方法 //event :事件
public void process(WatchedEvent event) {
System.out.println("事件发生了,详情如下:");
System.out.println(event.getType());
System.out.println(event.getState());
System.out.println(event.getPath());
if (event.getState() == KeeperState.SyncConnected) {
System.out.println("客户机与zk连接成功。。。。");
connectSignal.countDown();
System.out.println("递减后的值" + connectSignal.getCount());
}
}
});
connectSignal.await();
System.out.println("主程序创建zk对象成功");
return zk;
}
public static void main(String[] args) throws IOException, InterruptedException {
ZookeeperConnection zc = new ZookeeperConnection();
ZooKeeper zk = zc.connect("192.168.176.132");
System.out.println(zk);
zc.close();
}
private void close() throws InterruptedException {
zk.close();
}
}
2.ZookeeperUtil类 用于实现Zookeeper客户端的操作
public class ZookeeperUtil1 {
private static ZooKeeper zk;
private static ZookeeperConnection zc = new ZookeeperConnection();
//静态块 程序一加载到jvm就执行
static {
try {
zk = zc.connect("192.168.176.132");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 创建节点
* @param path
* @param data
* @throws KeeperException
* @throws InterruptedException
*/
public static void create(String path, byte[] data) throws KeeperException, InterruptedException {
zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* Return the data and the stat of the node of the given path.
* get
*/
public static void get(String path, boolean watch) throws KeeperException, InterruptedException {
Stat stat = zk.exists(path, false);
byte[] data = zk.getData(path,false, stat );
String v = new String(data,0,data.length);
System.out.println("节点 "+path+" 的值是 "+v);
}
/**
* set
*/
public static void set( String path,String content , int version) throws KeeperException, InterruptedException {
byte[] data = content.getBytes();
zk.setData(path, data,-1);
}
/**
* getAcl
* Return the ACL and stat of the node of the given path.
*/
public static void getAcl( String path) throws KeeperException, InterruptedException {
Stat stat = zk.exists(path, false);
List<ACL> acl = zk.getACL(path, stat );
System.out.println(acl);
}
public static void setAcl(String path, int version) throws KeeperException, InterruptedException {
zk.setACL(path,ZooDefs.Ids.OPEN_ACL_UNSAFE, version);
}
/**
* 递归删除节点 等效于Zookeeper里面的rmr deleteall
* @param path
* @param version = cversion
* if the given version is -1, it matches any node's versions
* @throws InterruptedException
* @throws KeeperException
*/
public static void rmr(String path,int version) throws InterruptedException, KeeperException {
zk.delete(path, version);
}
/**
* Return the stat of the node of the given path
* whether need to watch this node
* @param path
* @throws InterruptedException
* @throws KeeperException
*/
public static void stat(String path) throws KeeperException, InterruptedException{
Stat s = zk.exists(path, false);
System.out.println("==============stat===========");
System.out.println(s);
}
public static void getChildren(String path, boolean watch) throws KeeperException, InterruptedException{
List<String> children = zk.getChildren(path, false);
System.out.println(path + " 的子节点有 "+ children);
}
}
八、Curator