文章目录
1.zookeeper基本概念
-
Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据, 然后接受观察 者 的 注 册, 一旦这些数据的状 态发 生变化 , Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。
-
zk的数据模型:树形结构(linux和hdfs也是树形结构)
-
ZK每个节点znode,有一个唯一的路径标识
(/home/hadoop/app/ruozedata.txt) -
znode
1)临时的 ephemeral 临时znode下面不能有子节点
普通的
sequential
2)永久的 persistent 可以有子节点
普通的
sequential 顺序编号目录节点 -
每个znode节点有各自的版本号
每个节点数据发生了变化,该节点的版本会加(乐观锁) -
ZK节点存储的数据量不宜过大,几K
-
znode可以设置访问权限
-
znode可以设置watcher:当节点数据发生变化时,可以通过监视器获取
zk特点
2.zookeeper配置
修改zoo.cfg的dataDir目录
- linux tmp目录下的文件30天自动清除,所以要修改
dataDir=/home/hadoop/data/zookeeper
3.zkCli
- 打开zk客户端前先zkServer.sh start
[hadoop@ruozedata000 zookeeper]$ bin/zkCli.sh
Connecting to localhost:2181
2019-09-06 19:57:57,050 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2019-09-06 19:57:57,054 [myid:] - INFO [main:Environment@100] - Client environment:host.name=ruozedata000
2019-09-06 19:57:57,054 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_144
2019-09-06 19:57:57,056 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation
2019-09-06 19:57:57,191 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@852] - Socket connection established to localhost/127.0.0.1:2181, initiating session
[zk: localhost:2181(CONNECTING) 0] 2019-09-06 19:57:57,231 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1235] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x16d066a4de10000, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
ls和ls2
- ls查看当前目录下的子节点
- ls2是两个命令的综合 ls+get
- stat和get一样
[zk: localhost:2181(CONNECTED) 2] ls /zookeeper
[quota]
[zk: localhost:2181(CONNECTED) 3] ls2 /zookeeper
[quota]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 4] get /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 5] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
- cZxid znode 编号
- mZxid 修改后的id
- cversion 子节点版本
- dataVersion 数据版本
- ephemeralOwner 是否是临时节点
- numChildren 孩子数量
创建节点
[zk: localhost:2181(CONNECTED) 6] create /ruoze ruoze-data
Created /ruoze
[zk: localhost:2181(CONNECTED) 8] get /ruoze
ruoze-data
cZxid = 0x2
ctime = Fri Sep 06 20:26:59 CST 2019
mZxid = 0x2
mtime = Fri Sep 06 20:26:59 CST 2019
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
创建临时节点
[zk: localhost:2181(CONNECTED) 10] create -e /ruoze/tmp ruoze-data
Created /ruoze/tmp
[zk: localhost:2181(CONNECTED) 11] get /ruoze
ruoze-data
cZxid = 0x2
ctime = Fri Sep 06 20:26:59 CST 2019
mZxid = 0x2
mtime = Fri Sep 06 20:26:59 CST 2019
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 1
[zk: localhost:2181(CONNECTED) 12] get /ruoze/tmp
ruoze-data
cZxid = 0x3
ctime = Fri Sep 06 20:29:06 CST 2019
mZxid = 0x3
mtime = Fri Sep 06 20:29:06 CST 2019
pZxid = 0x3
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x16d066a4de10000
dataLength = 10
numChildren = 0
- 客户端退出后重新进入,刚刚创建的临时节点没有了
sequential
[zk: localhost:2181(CONNECTED) 1] create -s /ruoze/seq seqdata
Created /ruoze/seq0000000001
[zk: localhost:2181(CONNECTED) 2] ls /ruoze
[seq0000000001]
[zk: localhost:2181(CONNECTED) 3] create -s /ruoze/seq seqdata
Created /ruoze/seq0000000002
[zk: localhost:2181(CONNECTED) 4] ls /ruoze
[seq0000000001, seq0000000002]
不可以递归创建目录
[zk: localhost:2181(CONNECTED) 6] create /ruoze/a/b/c abc
Node does not exist: /ruoze/a/b/c
版本号变化
[zk: localhost:2181(CONNECTED) 7] get /ruoze
ruoze-data
cZxid = 0x2
ctime = Fri Sep 06 20:26:59 CST 2019
mZxid = 0x2
mtime = Fri Sep 06 20:26:59 CST 2019
pZxid = 0x7
cversion = 4
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 2
[zk: localhost:2181(CONNECTED) 8] set /ruoze new-ruozedata
cZxid = 0x2
ctime = Fri Sep 06 20:26:59 CST 2019
mZxid = 0x9
mtime = Fri Sep 06 20:40:51 CST 2019
pZxid = 0x7
cversion = 4
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 13
numChildren = 2
watch
- zk中的watcher是一次性的
- NodeCreated
NodeDataChanged
NodeDeleted
NodeChildrenChanged
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 3] stat /ruoze watch
Node does not exist: /ruoze
[zk: localhost:2181(CONNECTED) 4] create /ruoze 123
WATCHER::
WatchedEvent state:SyncConnected type:NodeCreated path:/ruoze
Created /ruoze
四字命令
- eg
[hadoop@ruozedata000 ~]$ echo conf|nc localhost 2181
clientPort=2181
dataDir=/home/hadoop/data/zookeeper/version-2
dataLogDir=/home/hadoop/data/zookeeper/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=0
[hadoop@ruozedata000 ~]$
4.API
- zookeeper
package com.ruozedata.bigdata.zookeeper;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ZookeeperApp implements Watcher {
private static Logger logger = LoggerFactory.getLogger(ZookeeperApp.class);
private static CountDownLatch connected = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ZooKeeper zk = new ZooKeeper("ruozedata000:2181", 5000, new ZookeeperApp());
logger.warn("客户端开始连接zk服务器....");
logger.warn("连接状态:{}", zk.getState());
connected.await();
// Thread.sleep(2000);
logger.warn("连接状态:{}", zk.getState());
//同步创建临时节点
// String path1 = zk.create("/zk-test-eph", "1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
//
//
// logger.warn("创建节点成功:{}", path1);
//
// Thread.sleep(10000);
//异步创建临时节点
// String ctx = "{'create','success'}";
//
// zk.create("/zk-test-eph", "1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, new AsyncCallback.StringCallback() {
// @Override
// public void processResult(int i, String path, Object ctx, String name) {
// logger.warn("创建节点成功:{}", name);
// }
// },ctx);
//
// Thread.sleep(10000);
//不知道当前版本号,可以设置为-1
// Stat stat = zk.setData("/ruoze", "ruozedata".getBytes(), -1);
// logger.warn("版本号:{}", stat.getVersion());
// zk.delete("/ruoze/123",-1);
//获取节点数据
// Stat stat = new Stat();
// byte[] data = zk.getData("/ruoze", true, stat);
//
// logger.warn("获取节点数据:{}", new String(data));
// logger.warn("版本号:{}", stat.getVersion());
//节点是否存在
// Stat stat = zk.exists("/ruoze", true);
// if(stat!=null){
// logger.warn("节点存在");
// }else {
// logger.warn("节点不存在");
// }
//获取节点列表
List<String> children = zk.getChildren("/ruoze", true);
for(String child:children){
logger.warn(child);
}
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
logger.warn("接收到通知:{}", watchedEvent);
connected.countDown();
}
}
}
5.面试重点
1.选举机制
-
半数机制:集群中半数以上机器存活,集群可用。所以 Zookeeper 适合安装奇数台 服务器。
-
Zookeeper 虽然在配置文件中并没有指定 Master 和 Slave。但是,Zookeeper 工作时, 是有一个节点为 Leader,其他则为 Follower,Leader 是通过内部的选举机制临时产生的。
-
以一个简单的例子来说明整个选举的过程。
假设有五台服务器组成的 Zookeeper 集群,它们的 id 从 1-5,同时它们都是最新启动的, 也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来 看看会发生什么,如图
(1)服务器 1 启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应, 所以它的选举状态一直是 LOOKING 状态。
(2)服务器 2 启动,它与最开始启动的服务器 1 进行通信,互相交换自己的选举结果, 由于两者都没有历史数据,所以 id 值较大的服务器 2 胜出,但是由于没有达到超过半数以 上的服务器都同意选举它(这个例子中的半数以上是 3), 所以服务器 1、2 还是继续保持 LOOKING 状态。
(3)服务器 3 启动,根据前面的理论分析,服务器 3 成为服务器 1、2、3 中的老大, 而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的 Leader。
(4)服务器 4 启动,根据前面的分析,理论上服务器 4 应该是服务器 1、2、3、4 中最 大的,但是由于前面已经有半数以上的服务器选举了服务器 3,所以它只能接收当小弟的命 了。
(5)服务器 5 启动,同 4 一样当小弟。
2.监听器原理
1、监听原理详解:
1)首先要有一个main()线程
2)在main线程中创建Zookeeper客户端,这时就会创建两个线 程,一个负责网络连接通信(connet),一个负责监听(listener)。
3)通过connect线程将注册的监听事件发送给Zookeeper。
4)在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中。
5)Zookeeper监听到有数据或路径变化,就会将这个消息发送 给listener线程。 6)listener线程内部调用了process()方法。
2、常见的监听
1)监听节点数据的变化
get path [watch]
2)监听子节点增减的变化
ls path [watch]