ZooKeeper
是一个为分布式应用所设计的开源协调服务。
ZooKeeper
是一个为分布式应用所设计的开源协调服务。它主要为用户提供同步、配置管理、分组和命名等服务,减轻分布式应用程序所承担的协调任务。ZooKeeper
的文件系统使用了我们所熟悉的目录树结构。ZooKeeper
是使用Java
编写的,但是它支持Java
和C
两种编程语言。
目标 | 说明 |
---|---|
简单化 | ZooKeeper 允许分布式的进程通过共享体系的命名空间来进行协调,ZooKeeper 采用Znode 搭建与标准文件系统类似的命名空间【可以参见下一章“ ZooKeeper 命名空间”】,且该命名空间是存放在内存中的,这就意味着ZooKeeper 具备高吞吐量、低延迟的能力。ZooKeeper 具备高性能、高可靠性以及严格的有序访问。 |
健壮性 | 使用心跳来检测服务器的状态。如果有服务器失联,就连接到其他备用服务器上。 |
有序性 | ZooKeeper 可以为每一次更新操作赋予一个版本号,并且此版本号是全局有序的,不存在重复的情况。 |
速度优势 | 读性能优于写性能。 |
原子性 | 在命名空间中,每一个Znode 的数据将被原子地读写。读操作将读取与Znode 相关的所有数据,写操作将替换掉所有的数据。每一个结点都有一个访问控制表,规定了用户操作的权限。 |
可靠性 | |
时效性 |
ZooKeeper拥有一个层次的命名空间。在命名空间中,每个结点称为Znode
,每个Znode
包含了它自身和它的子节点相关联的数据。指向结点的路径必须使用规范的绝对路径表示,并且以“/”来分隔。【在ZooKeeper
中不允许使用相对路径来表示】
每个Znode
维护着一个属性结构,包含了数据的版本号dataVersion
,时间戳ctime、mtime
等状态信息。
[zk: localhost:2181(CONNECTED) 14] get /app2
aaabbb
cZxid = 0x600000004 # 创建事务编号
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000004 # 修改事务编号
mtime = Thu Sep 13 21:47:07 EDT 2018
pZxid = 0x600000004 # 持久化事务编号
cversion = 0 # 创建版本
dataVersion = 0
aclVersion = 0 # 权限版本
ephemeralOwner = 0x65d5b998e80000
dataLength = 6 # 数据长度
numChildren = 0 # 子节点个数
看不懂这些属性,可以参见【Znode 属性】
特征 | 说明 |
---|---|
Watch |
客户端可以在节点上设置Watch ,当节点的数据发生变化(增删改等操作)将会出发Watch 的对应的操作。【Watch 有且仅会被触发一次并发送一个通知。】 |
数据访问 | ZooKeeper 中的每个节点上存储的数据需要被原子性的操作。 |
临时节点 | ZooKeeper 中的节点有两种,分别为临时节点和永久节点。节点的类型在创建后不能改变。ZooKeeper 临时节点的生命周期依赖于创建它们的Session 。一旦Session 结束,临时节点将被自动删除,当然也可以手动删除。ZooKeeper 的临时节点不允许拥有子节点。相反,永久节点的生命周期不依赖于Session ,并且只有在客户端显示执行删除操作的时候,它们才被删除。 |
顺序结点 | 【唯一性】当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为“%010d”(10位数字,没有数值的数据位用0填充,例如0000000001)。当计数值大于232-1时,计数器将会溢出。 |
属性 | 说明 |
---|---|
czxid | 节点被创建的Zxid值。 |
mzxid | 节点被修改的Zxid值。 |
ctime | 节点被创建的时间。 |
mtime | 节点被修改的时间。 |
version | 节点被修改的版本号。 |
cversion | 节点所拥有的子节点被修改的版本号。 |
aversion | 节点的ACL被修改的版本号。 |
ephemeralOwner | 如果此节点是临时节点,那么它的值就是这个节点拥有者的Session ID。否则,它的值为0。 |
numChildren | 节点拥有的子节点的个数。 |
ZooKeeper使用ACL对Znode进行访问控制。ACL拥有三种模式:
一个ACL和一个ZooKeeper节点是对应的,并且不存在继承关系,相互独立。
权限 | 权限描述 |
---|---|
CREATE | 创建子节点。 |
READ | 从节点获取数据或者列出节点的所有子节点。 |
WRITE | 设置节点的数据。 |
DELETE | 删除子节点。 |
ADMIN | 可以设置权限。 |
模式 | 说明 |
---|---|
world | 代表某一特定的用户(Client)。 |
auth | 代表任何已经通过验证的用户(Client)。 |
digest | 通过用户密码进行验证。 |
ip | 通过Client IP地址进行验证。 |
一致性特点 | 说明 |
---|---|
顺序一致性 | Client的更新顺序与它们发送的顺序相一致的。 |
原子性 | 更新操作只有失败和成功两种结果。 |
单系统镜像 | ZooKeeper视图在不同服务器上都一致。 |
可靠性 | 一旦一个更新操作被应用,那么在更新之前,其值都不会改变。 |
实时性 | 在特定的一段时间内,客户端看到的系统需要被保证是实时的(在十几秒的时间里)。在此时间段内,任何系统的改变将被客户端看到,或者被客户端侦测到。 |
首先,当然是先下载环境啦~点击跳转到下载页面
vim /etc/sysconfig/network
HOSTNAME=hadoop
wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz
tar -zxvf zookeeper-3.4.10.tar.gz
/opt
目录下:
sudo mv zookeeper-3.4.10 /opt
cd /opt
sudo mv zookeeper-3.4.10 zookeeper
cd conf
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
dataDir
目录:
mkdir /opt/zookeeper/zooData
dataDir=/tmp/zookeeper
为/opt/zookeeper/zooData
mkdir /opt/zookeeper/zooLog
dataLogDir=/opt/zookeeper/zooLog
vim /ect/hosts
192.168.80.8 node0
192.168.80.9 node1
192.168.80.10 node2
192.168.80.11 node3
server.1=node1:2888:3888
server.2=node2:2888:3888
server.3=node3:2888:3888
myid
:
myid
不允许相同。myid
就是上述的server.数字
的数字,即server.1
中的1
就是myid
的值。echo 0 > /opt/zookeeper/zooData/myid
ssh-keygen
,一路回车即可。~/.ssh
文件夹,创建一个touch authorized_keys
文件,将需要SSH免登录的服务端的公钥id_rsa.pub
中的内容复制到authorized_keys
中即可。zookeeper
文件夹分发到其他的服务端上,记得修改myid
,和按照上修改配置文件部分重新修改一遍。
scp -r /opt/zookeeper root@node1:/opt/
systemctl stop firewalld && systemctl disable firewalld
service iptables stop
全部配置完毕之后,就是启动
zookeeper
了。
如果启动不成功的话,可以看看zookeeper/bin/zookeeper.out
下的日志输出。
./bin/zkServer.sh start
./bin/zkServer.sh status
[root@node3 bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
leader
是Master
服务器,这个是选举出来的,如果Master
挂掉,会重新随机选举出来。follower
是Slave
服务器。[root@node3 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: leader
[root@node2 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
kill -9
掉leader
,那么其他节点就会重新投票产生leader
。Error contacting service. It is probably not running.
使用
./bin/zkCli.sh
启动命令行,使用help
可以查看帮助文档。
ls path [watch]
ls 路径 监听
[zk: localhost:2181(CONNECTED) 6] ls /
[zookeeper]
create [-s] [-e] path data acl
create 序号 结点类型 路径 数据 权限
ephemeral
:断开连接就会删除自身结点。persistent
:断开连接不删除自身结点。[zk: localhost:2181(CONNECTED) 6] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 7] create /app1 aaa
Created /app1
[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper, app1]
[zk: localhost:2181(CONNECTED) 9] create -s /app2 aaa
Created /app20000000001
[zk: localhost:2181(CONNECTED) 10] ls /
[app20000000001, zookeeper, app1]
[zk: localhost:2181(CONNECTED) 11] create -e /app2 aaabbb
Created /app2
[zk: localhost:2181(CONNECTED) 12] ls /
[app20000000001, zookeeper, app2, app1]
get path [watch]
,获取数据
get 路径 监听
[zk: localhost:2181(CONNECTED) 14] get /app2
aaabbb
cZxid = 0x600000004 # 创建事务编号
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000004 # 修改事务编号
mtime = Thu Sep 13 21:47:07 EDT 2018
pZxid = 0x600000004 # 持久化事务编号
cversion = 0 # 创建版本
dataVersion = 0
aclVersion = 0 # 权限版本
ephemeralOwner = 0x65d5b998e80000
dataLength = 6 # 数据长度
numChildren = 0 # 子节点个数
set path data [version]
,设置结点数据
set 路径 数据 [版本号]
# 设置数据为Hello_world
[zk: localhost:2181(CONNECTED) 15] set /app2 Hello_world
cZxid = 0x600000004
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000005
mtime = Thu Sep 13 22:17:11 EDT 2018
pZxid = 0x600000004
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x65d5b998e80000
dataLength = 11
numChildren = 0
[zk: localhost:2181(CONNECTED) 16] get /app2
Hello_world # 重新设置的数据
cZxid = 0x600000004
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000005
mtime = Thu Sep 13 22:17:11 EDT 2018
pZxid = 0x600000004
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x65d5b998e80000
dataLength = 11
numChildren = 0
delete path [version]
delete 路径 版本号
[zk: localhost:2181(CONNECTED) 18] delete /app2
[zk: localhost:2181(CONNECTED) 19] ls /app2
Node does not exist: /app2
监听数据的变化
get path [watch]
get 路径 监听器
import org.apache.log4j.BasicConfigurator;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
public class Test1 {
private static ZooKeeper zk = null;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// 配置Log4J
BasicConfigurator.configure();
// 建立连接
int sessionTimeout = 2000;
// 注意:node1、node2、node3需要在/etc/hosts中配置以下内容
// 192.168.80.9 node1
// 192.168.80.10 node2
// 192.168.80.11 node3
String connectString = "node1:2181,node2:2181,node3:2181";
zk = new ZooKeeper(connectString, sessionTimeout, watchedEvent -> System.out.println("时间触发……"));
}
/**
* create方法参数
* String path, byte[] data, List acl, CreateMode createMode
* path:路径
* data:值bytes
* acl:对节点的访问控制
* createMode:节点的类型——短暂,永久,序号
*/
@Test
public void create() throws KeeperException, InterruptedException {
String path = zk.create("/app3", "hello_world".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(path);
}
/**
* 判断节点是否存在
*/
@Test
public void exist() throws KeeperException, InterruptedException {
String nodeName = "/app1";
// 设置为true会调用zk中的监听器
Stat stat = zk.exists(nodeName, true);
if (stat != null) {
System.out.println("节点“" + nodeName + "”存在,长度为:" + stat.getDataLength());
} else {
System.out.println("节点" + nodeName + "不存在的。");
}
}
/**
* 获取子节点
*/
@Test
public void getChildren() throws KeeperException, InterruptedException {
List<String> list = zk.getChildren("/", true);
for (String str : list) {
System.out.println(str);
}
}
/**
* 获取子节点
* getChildren(path, watch) 监听的事件是:节点下的子节点增减变化事件。
*/
@Test
public void getChildren1() throws KeeperException, InterruptedException {
List<String> list = zk.getChildren("/", watchedEvent -> System.out.println("监控节点下的子节点被改变->" + watchedEvent.getPath()));
for (String str : list) {
System.out.println(str);
}
Thread.sleep(Long.MAX_VALUE);
}
/**
* 获取节点的内容
* getData(path,watch) 监听的事件是:节点数据变化事件。
*/
@Test
public void getData1() throws KeeperException, InterruptedException {
byte[] bytes = zk.getData("/app1", watchedEvent -> System.out.println("数据改变了……"), null);
System.out.println(new String(bytes));
}
/**
* 获取节点的内容
*/
@Test
public void getData() throws KeeperException, InterruptedException {
byte[] bytes = zk.getData("/app1", false, null);
System.out.println(new String(bytes));
}
/**
* 修改节点的内容
* version为-1时表示自动维护
*/
@Test
public void setData() throws KeeperException, InterruptedException {
Stat stat = zk.setData("/app1", "test".getBytes(), -1);
System.out.println(stat.toString());
}
/**
* 删除节点,非空节点删除不掉。
* version为-1时表示自动维护
*/
@Test
public void delete() throws KeeperException, InterruptedException {
zk.delete("/app1", -1);
}
}