zookeeper安装与基本使用
概念
基本概念
zookeeper 是apache 提供了一个开源的分布式协调服务框架。zookeeper可以从设计模式的角度理解为观察者模式设计的分布式服务管理框架。它被当作一个服务信息的注册中心,注册者对它进行信息的注册,然后接受观察,一旦注册的服务发生改变,就把更新推送给观察者。因此可以把它理解为:zookeeper=文件系统+通知机制。
特点
1.一个领导者leader和多个跟随者follower组成的工作集群。
2.全局数据一致,zookeeper中集群各个节点存储的数据信息相同,每个服务节点server保存的都是相同的数据副本。
3.数据更新原子性,数据更新是一个原子性操作,要么所有节点都更新成功,要么都更新失败。
4.更新请求顺序执行,来自同一个客户端 client的更新请求按其发送顺序依次执行
5.数据实时性,在一定时间范围内,客户端client能读取到最新的数据
6.leader 负责进行投票的发起和决议,更新系统状态。
7.follower可以同 leader一样,用于接收客户端请求并向客户端返回结果(相同数据副本),在选举leader过程中参与投票
8.zookeeper 集群中只要有半数以上节点存活,zookeeper 集群就可正常对外提供服务。
9.zookeeper 集群的数据发生更新时,主动推送给观察zookeeper 集群的观察者数据发生了改变。
数据结构
zookeeper 底层数据存储数据采用了 树 的数据结构,与 unix 文件系统存储文件的结构类似。zookeeper 树中的每个节点被称为 znode ,znode 用来存储和唯一标识数据,znode 默认存储 1M 数据。访问 znode 相对于访问唯一标识的路径。

zookeeper应用场景
服务节点动态上下线
hadoop HA 中 双 namenode 或 双 resourcemanger 的配置,通过zookeeper 作为服务节点的注册中心,自动感知节点的信息健康状况,节点挂掉后,zookeeper 通知hadoop 集群,自动实现了节点的转换,保证了hadoop集群的高可用。

RPC 框架dubbo 中,zookeeper 根据本身得分特性,作为服务注册中心,负责保存 dubbo 服务的元数据信息,并可以基于长连接通知观察者服务的上下线。

统一命名服务

统一配置管理

统一集群管理

软负载均衡

数据发布订阅

总的来说,zookeeper 的应用场景基本都是符合其特性的,集群数据同步(各节点数据信息一致)和数据更新时自动向观察zookeeper 集群的观察者推送更新。
安装部署
下载地址:https://zookeeper.apache.org/releases.html
单机部署
安装zookeeper 需要依赖于 java 环境,JDK 环境安装前面已经提及多变,这里不再赘述
利用xshell解压上传 zookeeper 压缩包到用户家目录

解压修改文件权限
# 解压文件
sudo tar -xzvf zookeeper-3.4.10.tar.gz -C /opt/module
# 文件重命名
sudo mv zookeeper-3.4.0.tar.gz zookeeper
# 修改文件权限
sudo chown -R hadoop ./zookeeper
修改配置文件
配置文件在 zookeeper 安装目录的 conf 目录下
重命名 zoo_sample.cfg 为 zoo.cfg
mv zoo_sample.cfg zoo.cfg
修改编辑 zoo.cfg 文件为
# 主要修改 zookeeper 数据存放的位置
dataDir=/opt/module/zookeeper/zkdata
clientPort=2181
创建存放数据文件
mkdir /opt/module/zookeeper/zkdata
修改环境变量
sudo vim /etc/profile
添加如下配置
##ZOOKEEPER_HOME
export ZOOKEEPER_HOME=/opt/module/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
export PATH=$PATH:$ZOOKEEPER_HOME/conf
环境变量生效
source /etc/profile
启动zookeeper 查看状态
# 启动 zookeeper 服务
zkServer.sh start
# 查看 zookeeper 进程信息
jps # 结果会有 jps QuorumPeerMain 两个进程
# 查看zookeeper 服务状态
zkServer.sh status # 显示结果为 Mode: standalone
# 进入 zookeeper 客户端 quit 推出客户端进程
zkCli.sh
# 查看 zookeeper 版本号
echo stat|nc localhost 2181
伪分布式部署
伪分布式部署的本质时在本地配置三个不同的配置文件,根据配置文件中配置的数据和日志存放位置不同,端口号不同来进行模仿分布式效果。实际上就是本地启动多个不同端口号,相互之间存在联系通信的zookeeper 进程
相同上传修改文件,配置环境变量这里就不在赘述,同上面一样
在zookeeper 安装目录下创建三个zoo1.cfg、zoo2.cfg、zoo3.cfg文件
cd /opt/module/zookeeper/conf
# 创建三个配置文件
touch zoo{1..3}.cfg
修改zoo1.cng 到 zoo3.cnf
zoo1.cnf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/module/zookeeper/zkdata1
dataLogDir=/opt/module/zookeeper/zkdata1
clientPort=2181
# node1 需要在 /etc/hosts 中配置 ip 与主机之间的映射
server.1=node1:20881:30881
server.2=node1:20882:30882
server.3=node1:20883:30883
zoo2.cnf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/module/zookeeper/zkdata2
dataLogDir=/opt/module/zookeeper/zkdata2
clientPort=2182
# node1 需要在 /etc/hosts 中配置 ip 与主机之间的映射
server.1=node1:20881:30881
server.2=node1:20882:30882
server.3=node1:20883:30883
zoo3.cnf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/module/zookeeper/zkdata3
dataLogDir=/opt/module/zookeeper/zkdata3
clientPort=2183
# node1 需要在 /etc/hosts 中配置 ip 与主机之间的映射
server.1=node1:20881:30881
server.2=node1:20882:30882
server.3=node1:20883:30883
分别为每个配置文件创建zkdata目录
mkdir /opt/module/zookeeper/zkdata{1..3}
在每个zkdata文件下创建 myid 文件(名称与zoo.cnf中 server后的编号对应,代表 zookepper 应用在 zookeeper集群中的编号)
# 三个文件都要配置,这里以 zkdata1 为例
cd /opt/module/zookeeper/zkdata1
touch myid
echo 1 > myid # 具体以实际编号为例
cat myid
启动伪分布式集群
# 启动不同的 zookeeper进程
zkServer.sh start conf/zoo1.cfg
zkServer.sh start conf/zoo1.cfg
zkServer.sh start conf/zoo1.cfg
# 查看 zookeeper 服务状态
zkServer.sh status conf/zoo1.cfg
zkServer.sh status conf/zoo2.cfg
zkServer.sh status conf/zoo3.cfg
分布式部署
zooker 集群部署最低需要三台机器,这里以三台机器为例
集群规划
sudo vim /etc/hosts
添加符合实际情况的主机与ip映射

上传 zookeeper 压缩包到 hadoop131 家目录,并进行解压赋予权限
sudo tar -xzvf zookeeper.tar.gz -C /opt/module
sudo chown -R hadoop:hadoop ./zookeeper

重命名 zookeeper 安装目录下的 conf 目录下的zoo_sample.cfg为zoo.cfg
mv /opt/module/zookeeper/conf/zoo_sample.cfg /opt/module/zookeeper/conf/zoo.cfg
配置conf 目录下zoo.cfg文件如下

配置conf 目录下文件如下log4j.properties文件

zookeeper安装目录下创建 zkdata
mkdir ./zookeeper/zkdata
# 创建日志记录文件
mkdir ./zookeeper/zkdata/logs
配置编辑服务器编号
touch ./zookeeper/zkdata/myid
echo 1 > ./zookeeper/zkdata/myid # 与服务器编号保持一致
修改 zookeeper安装目录 bin 下的zkEnv.sh(修改日志记录位置)

集群分发 zookeeper
scp -r /opt/module/zookeepe root@hadoop132:/opt/module/
scp -r /opt/module/zookeepe root@hadoop133:/opt/module/
修改分发机器的 myid
# 修改 hadoop132 机器与zookeeper 中配置的zookeeper 编号保持一致
echo 2 > /opt/module/zookeeper/zkdata/myid
# 修改 hadoop133 机器与zookeeper 中配置的zookeeper 编号保持一致
echo 3 > /opt/module/zookeeper/zkdata/myid
分别在每台机器上配置 zookeeper 环境变量(同上一样)
分别在每台机器上启动zookeeper(前提配置环境变量)
zkServer.sh start
分别在每台机器上查看 zookeeper 进程和状态
zkServer.sh status
jps

zookeeper 操作
命令操作
启动客户端
zkCli.sh

API 操作
统一maven pom依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</dependency>
</dependencies>
zookeeper 基本 API 操作
test 类
package com.ldy.zk;
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;
public class Testzk {
// 集群可以多个节点通过 , 进行并列(连接)
private static String connect="192.168.0.115:2181";
private static int sessionTimeout=2000;
private ZooKeeper zkclient;
//构造zookeeper客户端
@Before
public void init() throws IOException {
zkclient=new ZooKeeper(connect, sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
// 收到事件通知后的回调函数(用户的业务逻辑)
System.out.println(watchedEvent.getType() + "--" + watchedEvent.getPath());
// 再次启动监听
try {
zkclient.getChildren("/", true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
//判断节点是否存在
@Test
public void exist() throws KeeperException, InterruptedException {
Stat exists = zkclient.exists("/hbase", false);
System.out.println(exists==null ? "不存在" : "存在");
}
//创建节点
@Test
public void create() throws KeeperException, InterruptedException {
// 参数1:要创建的节点的路径; 参数2:节点数据 ; 参数3:节点权限 ;参数4:节点的类型
String nodeCreated = zkclient.create("/atguigu", "jinlian".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
//获取子节点并监听节点变化
public void getchilds() throws KeeperException, InterruptedException {
List<String> children = zkclient.getChildren("/", true);
for(String child:children){
System.out.println(child);
}
//延时阻塞
Thread.sleep(Long.MAX_VALUE);
}
@After
//关闭连接
public void close() throws InterruptedException {
if(zkclient!=null){
zkclient.close();
System.out.println("释放连接资源");
}
else return;
}
}
zookeeper 监听服务上下线实例
Client 类
package com.ldy.zk;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Client {
private String connectUrl="master:2181";
private int sessionTimeout=2000;
//连接需要释放资源
private ZooKeeper zkClient;
//获取连接
public void getconnect() throws IOException {
zkClient=new ZooKeeper(connectUrl, sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
try {
getchilds();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//监听节点上下线
public void getchilds() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren("/servers", true);
List<String> list=new ArrayList<String>();
for (String child : children) {
byte[] data = zkClient.getData("/servers/" + child, false, null);
list.add(new String(data));
}
System.out.println("------上线节点信息-----");
for (String s : list) {
System.out.println(s+"已经上线");
}
}
//业务处理代码
public void process() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
Client client=new Client();
//获取连接
client.getconnect();
//监听节点上下线
client.getchilds();
//业务处理
client.process();
}
}
ResigstServer 类
package com.ldy.zk;
import org.apache.zookeeper.*;
import java.io.IOException;
import static java.lang.Long.MAX_VALUE;
public class ResigstServer {
private String connectUrl="master:2181";
private int sessionTimeout=2000;
//连接需要释放资源
private ZooKeeper zkClient;
//获取zookeeper连接
public void getconnect() throws IOException {
zkClient=new ZooKeeper(connectUrl, sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
}
});
}
//注册服务
public void registserver(String hostname) throws KeeperException, InterruptedException {
// 创建一个 znode 节点
String path = zkClient.create("/servers/server", hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("-----------------");
System.out.println(hostname+"节点已经上线");
}
//业务处理
public void process() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
//获取zookeeper连接
ResigstServer resigstServer=new ResigstServer();
resigstServer.getconnect();
//注册服务
resigstServer.registserver(args[0]);
//业务处理
resigstServer.process();
}
}