zookeeper
Zookeeper介绍
见zookeeper原理.md
Zookeeper作用?
Zookeeper是一个高效的分布式协调服务,可以提供配置信息管理、命名、分布式同步、集群管理、数据库切换等服务。它不适合用来存储大量信息,可以用来存储一些配置、发布与订阅等少量信息。
Zookeeper应用场景:
Hadoop、Storm、消息中间件、RPC服务框架、分布式数据库同步系统都需要依赖Zookeeper做信息同步
简介:
客户端发起事务请求,事务请求的结果在整个Zookeeper集群中所有机器上的应用情况是一致的。不会出现集群中部分机器应用了该事务,而存在另外一部分集群中机器没有应用该事务的情况。
在Zookeeper集群中的任何一台机器,其看到的服务器的数据模型是一致的。Zookeeper能够保证客户端请求的顺序,每个请求分配一个全局唯一的递增编号,用来反映事务操作的先后顺序。Zookeeper将全量数据保存在内存中,并直接服务于所有的非事务请求,在以读操作为主的场景中性能非常突出。
Zookeeper中的进程:
Zookeeper集群中的节点,根据其身份特性分为leader、follower、observer。leader负责客户端writer类型的请求;follower负责客户端reader类型的请求,并参与leader选举;observer是特殊的follower,可以接收客户端reader请求,但是不会参与选举,可以用来扩容系统支撑能力,提高读取速度
Zookeeper是一个基于观察者模式设计的分布式服务管理框架,负责存储和管理相关数据,接收观察者的注册。一旦这些数据的状态发生变化,zookeeper就负责通知那些已经在zookeeper集群进行注册并关心这些状态发生变化的观察者,以便观察者执行相关操作。
zookeeper的集群安装
在master虚拟机
1.上传jar包并解压
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz
2.重命名解压文件夹
mv apache-zookeeper-3.5.7-bin zookeeper-3.5.7
3.配置
(1)重命名zoo_sample.cfg
mv zoo_sample.cfg zoo.cfg
(2)修改zoo.cfg
vim zoo.cfg
修改dataDir为
dataDir=/usr/local/soft/zookeeper-3.5.7/data
(结束修改要创建该目录)
在最后面加上
server.0=master:2888:3888
server.1=node1:2888:3888
server.2=node2:2888:3888
(3)进入/usr/local/soft/zookeeper-3.5.7/data目录下
创建文件myid
vim myid
输入文件内容为0(对应上面的server.0)
(4)将安装目录分发到node1和node2中
scp -r zookeeper-3.5.7 node1:`pwd`
scp -r zookeeper-3.5.7 node2:`pwd`
(5)到node1和node2修改myid,将0分别改为1和2
(6)配置环境变量,每个虚拟机都要
vim /etc/profile
最后一行加上:
export ZOOKEEPER_HOME=/usr/local/soft/zookeeper-3.5.7
export PATH=$ZOOKEEPER_HOME/bin:$PATH
使其生效
source /etc/profile
(7)启动并查看状态(每个都需要)
zkServer.sh start
zkServer.sh status
Zookeeper命令(客户端操作)
查看路径下的节点
ls path
创建节点
create 节点 --> 创建普通节点
create 节点 节点数据 --> 创建普通节点并携带数据
create -s 节点 --> 创建节点并在节点名称后添加编号
create -e 节点 --> 创建临时节点
获取节点中的数据
get 节点
get -s 节点 查看节点中的数据并获取详细状态信息
get -w 节点 表示监听某个节点中的变化(只会触发一次效果)设置节点中的数据
set 节点 数据 --> 给节点重新赋予数据
获取节点状态信息
stat 节点
删除节点
delete 节点 – 可以删除一个不为空的节点(没有子节点)
deleteall 节点
退出客户端
quit
在/usr/local/soft/zookeeper-3.5.7目录下
zkCli.sh
当看到如下输出信息时,表示已经成功连接上本地的ZooKeeper服务器了:
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
上面的命令没有显式地指定ZooKeeper服务器地址,默认是连接本地的ZooKeeper服务器。如果希望连接指定的ZooKeeper服务器,可以通过如下方式实现:
zkCli.sh -server ip:port
例如:
zkCli.sh -server node1:2181
查看路径下的节点
ls path
ls /
创建节点
create 节点 --> 创建普通节点
create /shell
create /shell/node1
create /shell/node2
ls /shell
create 节点 节点数据 --> 创建普通节点并携带数据
create /shell/node1/next_node1 "node1 value"
获取节点中的数据
get 节点
get /shell/node1/next_node1
create -s 节点 --> 创建节点并在节点名称后添加编号
create -s /shell/node1
create -e 节点 --> 创建临时节点
create -e /shell/node3
作用:关闭了zookeeper节点重启就没有了
get -s 节点 查看节点中的数据并获取详细状态信息
get -s /shell/node1/next_node1
set 节点 数据 --> 给节点重新赋予数据
set /shell/node1/next_node1 "nodes value"
get -w 节点 表示监听某个节点中的变化(只会触发一次效果)
get -w /shell/node1/next_node1
Zookeeper的API使用
添加Pom依赖:
````
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.7</version>
</dependency>
````
实现功能:
①创建永久节点或临时节点
②获取节点数据
③获取当前节点的子节点
④监听节点变化
⑤删除节点
连接zookeeper
package com.shujia;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class Code01ZookeeperCon {
public static void main(String[] args) throws IOException, InterruptedException {
String connection = "master:2181,node1:2181,node2:2181";
int sessionTimeoutm = 1000 * 20;
/**
* sessionTimeoutm session timeout in milliseconds
* 会话超时时间,毫秒为单位
*
* Watcher watcher 表示一个观察者,可以观察当前连接的状态 该Watcher是一个接口
* 需要使用匿名内部类方式创建其对象,并实现其process方法 当连接发生变化时,就会执行process中定义的逻辑
*
*/
ZooKeeper zooKeeper = new ZooKeeper(connection, sessionTimeoutm, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("当前连接发生变化...");
}
});
System.out.println("当前行被执行..");
zooKeeper.close();
/*
执行结果:
当前行被执行..
当前连接发生变化...
当前连接发生变化...
当前行被执行 -> 当zooKeeper对象被创建后,就可以往下执行
1.当前连接发生变化 -> zooKeeper连接是异步发生的 创建好连接 会执行
2.当前连接发生变化 -> 连接被关闭时,连接发生变化 也会被执行
*/
}
}
API操作
package com.shujia;
import org.apache.zookeeper.*;
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;
import static com.oracle.jrockit.jfr.ContentType.Bytes;
public class Code02ZookeeperAPI {
ZooKeeper zooKeeper = null;
@Before
public void getCon() throws IOException {
String connection = "master:2181";
int sessionTimeoutm = 1000 * 30;
zooKeeper = new ZooKeeper(connection, sessionTimeoutm, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("当前连接发生变化...");
}
});
}
@Test
public void createNode() throws InterruptedException, KeeperException {
/**
* public String create(final String path, byte data[], List<ACL> acl,
* CreateMode createMode)
* throws KeeperException, InterruptedException
* ZooDefs.Ids.OPEN_ACL_UNSAFE 可以返回List<ACL> 对应是对节点基于所有权限
*
* CreateMode
* PERSISTENT:持久化节点 普通节点 会一直存在
* EPHEMERAL:临时节点 当连接被关闭,创建的节点会消失
* PERSISTENT_WITH_TTL: 带有生命周期节点
*
*/
String path = "/api";
String data = "create api";
zooKeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
public void createEphemeralNode() throws InterruptedException, KeeperException {
/**
* 对于临时节点,当会话连接关闭后,当前节点就会消失
*/
zooKeeper.create("/api/ephemeralNode", "ephemeral Node".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
Thread.sleep(10 * 1000);
}
/*
获取节点数据
*/
@Test
public void getNodeData() throws InterruptedException, KeeperException {
/*
public byte[] getData(String path, boolean watch, Stat stat)
watch 参数就相当于 get -w 命令
Stat stat 应该是当前路径的节点对应的状态信息
如果查看节点数据,要求节点必须先存在
*/
String path = "/api";
// 结果为stat 那么如何判断是否存在?
// 如果路径不存在,那么返回结果为null
Stat stat = zooKeeper.exists(path, false);
if (stat != null) {
byte[] bytes = zooKeeper.getData(path, false, stat);
String nodeData = new String(bytes);
System.out.println(nodeData);
}
// zooKeeper.getData(path,false,)
}
//获取当前节点的子节点
@Test
public void getChild() throws InterruptedException, KeeperException {
// List<String> children = zooKeeper.getChildren("/api", false);
// for (String child : children) {
// System.out.println(child);
// }
// 遍历节点及子节点
getAllChild("/api");
}
public void getAllChild(String path) throws InterruptedException, KeeperException {
List<String> children = zooKeeper.getChildren(path, false);
if (children.size() > 0) {
for (String child : children) {
System.out.println("获取到子节点:" + path + "/" + child);
getAllChild(path + "/" + child);
}
}
}
// 监听节点变化
@Test
public void getChange() throws InterruptedException, KeeperException {
byte[] data = zooKeeper.getData("/api", new Watcher() {
@Override
public void process(WatchedEvent event) {
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("当前节点发生变化...");
}
}, zooKeeper.exists("/api", false)
);
System.out.println(new String(data));
}
@Test
public void deleteNode() throws InterruptedException, KeeperException {
// -1 表示不指定具体版本 删除最新节点
// zooKeeper.delete("/api/node1/next_node1",-1);
// KeeperErrorCode = Directory not empty for /api
zooKeeper.delete("/api",-1);
// 递归迭代删除当前节点下所有节点 -- 小作业自己完成
}
@After
public void close() throws InterruptedException {
zooKeeper.close();
}
}
基于Zookeeper构建HA
HDFS的HA,指的是在一个集群中存在两个NameNode,分别运行在独立的物理节点上。在任何时间点,只有一个NameNodes是处于Active状态,另一种是在Standby状态。 Active NameNode负责所有的客户端的操作,而Standby NameNode用来同步Active NameNode的状态信息,以提供快速的故障恢复能力。
为了保证Active NN与Standby NN节点状态同步,即元数据保持一致。除了DataNode需要向两个NN发送block位置信息外,还构建了一组独立的守护进程”JournalNodes”,用来同步Edits信息。当Active NN执行任何有关命名空间的修改,它需要持久化到一半以上的JournalNodes上。而Standby NN负责观察JNs的变化,读取从Active NN发送过来的Edits信息,并更新自己内部的命名空间。一旦ActiveNN遇到错误,Standby NN需要保证从JNs中读出了全部的Edits,然后切换成Active状态。
使用HA的时候,不能启动SecondaryNameNode,会出错。
两个NameNode,分别运行在独立的物理节点上。在任何时间点,只有一个NameNodes是处于Active状态,另一种是在Standby状态。 Active NameNode负责所有的客户端的操作,而Standby NameNode用来同步Active NameNode的状态信息,以提供快速的故障恢复能力。
为了保证Active NN与Standby NN节点状态同步,即元数据保持一致。除了DataNode需要向两个NN发送block位置信息外,还构建了一组独立的守护进程”JournalNodes”,用来同步Edits信息。当Active NN执行任何有关命名空间的修改,它需要持久化到一半以上的JournalNodes上。而Standby NN负责观察JNs的变化,读取从Active NN发送过来的Edits信息,并更新自己内部的命名空间。一旦ActiveNN遇到错误,Standby NN需要保证从JNs中读出了全部的Edits,然后切换成Active状态。
使用HA的时候,不能启动SecondaryNameNode,会出错。
详见HA步骤.md