1. ZK简介
一个分布式的,开放源码的分布式应用程序协调服务。
2. ZK数据模型
2.1 模型结构
2.2 模型的特点
- 每个子目录如/node1都被称作一个znode(节点)。这个znode是它所在的路径唯一标识
- znode可以有子节点目录,并且每个znode可以存储数据
- znode是有有版本的,每个znode中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
- znode是可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端
3. 节点的分类
3.1 持久节点(PERSISTENT)
指在节点创建后,就一直存在,知道有删除操作来主动删除这个节点–会因为创建该节点的客户端会话失效而消失
3.2 持久顺序节点(PERSISTENT_SEQUENTIAL)
这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。
3.3 临时节点(EPHEMERAL)
和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点。
3.4 临时顺序节点(EPHEMERAL_SEQUENTIAL)
具有临时节点特点额外的特性是,每个父节点会为他的第一级子节点维护一份时序。这点和刚才提到的持久顺序节点类似
4. 安装
4.1 linux系统安装
1 2
| tar -zxvf jdk-8u171-linux-x64.tar.gz mv ./jdk1.8.0_171/ /usr/java/
|
1 2
| vim /etc/profile source /etc/profile
|
1
| tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz
|
由于zk默认加载的是zoo.cfg
,所以需要改名
1
| mv zoo_sample.cfg zoo.cfg
|
由于zookeeper加载要将节点加载到磁盘,所以需要预先新建一个磁盘目录
1 2 3 4 5 6 7
| [root@localhost bin] ZooKeeper JMX enabled by default Using config: /root/apache-zookeeper-3.6.3-bin/conf/zoo.cfg Starting zookeeper ... STARTED
jps
|
QuorumPeerMain就是zk
1
| ./zkCli.sh -server 192.168.9.3:2181
|
如果是本机,-server后可不加
5. 配置文件说明
tickTime
:集群节点心跳时间,
initLimit
:初始化集群时集群节点同步超时时间20s
syncLimit
:集群在运行过程中,同步数据超时时间为10s(这里5是指5次心跳)
dataDir
:默认数据存储位置
clientPort
:zk服务监听端口号
maxClientCnxns
:线程池线程数量
6. 客户端基本指令
1 2 3 4 5
| create path data - create path data - create -s path data - create -e path data - create -e -s path data
|
quit
:会直接导致会话断开,会话失效,其他ctrl+c会导致触发倒计时
注:临时节点上不能创建任何节点
cZxid
:创建事务ID
ctime
:创建时间
mZxid
:修改ID
mtime
:修改时间
pZxid
:父结点版本号
cversion
:创建版本号
dataVersion
:数据版本号
aclVersion
:
ephemerslOwner
:是否是临时节点
dataLength
:存储数据长度
numChildren
:子节点数量
只能删除没有子节点的节点,如果有子节点,则无法删除
若想删除有子节点的,则需要deleteall
7. watch节点监听机制
监听分为节点目录监听和节点数据监听
- 目录监听是监听节点目录的变化
- 数据监听是监听当前节点数据的变化
两种监听都是一次性的,也就是发现一个修改,下次修改则不会触发监听机制
再次创建,无触发
8. Java操作ZK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| package com.w1nd.test;
import org.I0Itec.zkclient.IZkChildListener; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.SerializableSerializer; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.junit.After; import org.junit.Before; import org.junit.Test;
import java.io.IOException; import java.util.List;
public class TestZKClient {
private ZkClient zkClient;
@Test public void testCreateNode() { zkClient.create("/node1", "xiaochen", CreateMode.PERSISTENT); zkClient.create("/node1/names", "zhangsan", CreateMode.PERSISTENT_SEQUENTIAL); zkClient.create("/node1/lists", "xiaoxiao", CreateMode.EPHEMERAL); zkClient.create("/node1/lists1", "xiaoming", CreateMode.EPHEMERAL_SEQUENTIAL);
}
@Test public void testDeleteNode() { boolean delete = zkClient.delete("/node1"); boolean b = zkClient.deleteRecursive("/node1"); }
@Test public void testFindNodes() { List<String> children = zkClient.getChildren("/"); for (String child : children) { System.out.println(child); } }
@Test public void testFindNodeData() { Object readData = zkClient.readData("/node3"); System.out.println(readData); }
@Test public void testFindNodeDataAndStat() { Stat stat = new Stat(); Object readData = zkClient.readData("/node1", stat); System.out.println(readData); System.out.println(stat); }
@Test public void testWriteData() { }
@Test public void testOnNodeDataChange() throws IOException { zkClient.subscribeDataChanges("/node1", new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { System.out.println("当前节点路径:" + s); System.out.println("当前节点变化后数据:" + o); } @Override public void handleDataDeleted(String s) throws Exception { System.out.println("当前节点路径:" + s); } }); System.in.read(); }
@Test public void testOnNodesChange() throws IOException { zkClient.subscribeChildChanges("/node1", new IZkChildListener() { @Override public void handleChildChange(String s, List<String> list) throws Exception { System.out.println("父节点名称:" + s); System.out.println("发生变更后孩子节点名称:"); for (String name: list) { System.out.println(name); } } }); System.in.read(); }
@Before public void before() { zkClient = new ZkClient("192.168.9.3:2181", 60000 * 30, 60000, new SerializableSerializer()); }
@After public void after() { zkClient.close(); }
public static void main(String[] args) {
} }
|
9. ZK集群
9.1 集群(cluster)
集群是指同一种软件服务的多个节点同时提供服务
集群解决了什么问题?
9.2 集群架构
9.3 集群搭建
- 在~目录下新建三个文件夹
1
| mkdir zkdata1 zkdata2 zkdata3
|
- 建立myid文件
1
| touch zkdata1/myid zkdata2/myid zkdata3/myid
|
- 为每个zk指定id
1 2 3
| echo "1" >> zkdata1/myid echo "2" >> zkdata2/myid echo "3" >> zkdata3/myid
|
- 添加配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13
| vim zkdata1/zoo.cfg vim zkdata2/zoo.cfg vim zkdata3/zoo.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/root/zkdata1 clientPort=3001 server.1=196.168.9.3:3002:3003 server.2=192.158.9.3:4002:4003 server.3=192.168.9.3:5002:5003
|
- 相关操作
1 2 3 4 5 6 7 8
| # 启动 ./bin/zkServer.sh start /root/zkdata1/zoo.cfg ./bin/zkServer.sh start /root/zkdata2/zoo.cfg ./bin/zkServer.sh start /root/zkdata3/zoo.cfg # 查看状态 ./bin/zkServer.sh status /root/zkdata1/zoo.cfg # 停止 ./bin/zkServer.sh stop /root/zkdata1/zoo.cfg
|