Zookeeper的特点:
1.Zookeeper: 一个领导者(Leader), 多个跟随者(Follower) 组成的集群
2.集群中只要有半数以上的节点存活,Zookeeper集群就能正常服务。
3.全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个server数据都是一致的。
4.更新请求顺序进行,来自同一个client的更新请求按期发送顺序依次执行
5.数据更新原子性,一次数据更新要么成功,要么失败
6.实时性,在一定时间范围内,Client能读到最新的数据。
应用场景:
1.统一命名服务
在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。
例如:Ip不容易记住,而域名容易记住
2.统一配置管理
1)分布式环境下,配置文件同步非常常见
1> 一般要求一个集群中,所有的节点的配置信息都是一致的,比如:Kafka集群
2> 对配置文件修改后,希望能够快速同步到各个节点上
2)配置管理可以交由Zookeeper实现
1> 可将配置信息写入Zookeeper上的一个Znode
2> 各个客户端服务器监听这个Znode
3> 一旦Znode中的数据被修改,Zookeeper将通知各个客户端服务器
统一集群管理:
分布式环境中,实时掌握每个节点的状态是必要的。
可根据节点实时状态做出一些调整
Zookeeper可以实现实时监控节点状态变化
可将节点信息写入Zookeeper上的一个ZNode
监听这个Znode可获取他的实时状态变化
服务器动态上下线:
客户端能实时洞察到服务器上下线的变化
软负载均衡:
在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新客户端的请求。
Zookepper的下载地址:
官网地址:https://zookeeper.apache.org
安装zookeeper:
1.把zookeeper-3.4.9.tar.gz上传到服务器
2.解压zookeeper-3.4.9.tar.gz软件包,重命名为zookeeper
tar -zxvf zookeeper-3.4.9.tar.gz
mv zookeeper-3.4.9 zookeeper
3.修改配置文件zoo.cfg
mv zookeeper/conf/zoo.simple.cfg zookeeper/conf/zoo.cfg
修改datadir路径:
dataDir=/opt/mysoft/zookeeper/zkData
创建制定路径的目录:zkData
mkdir zkData
4.启动服务器:
bin/zkServer.sh start
jps: QuorumPeerMain
5.启动客户端:
bin/zkCli.sh
选举机制:
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
服务器5启动,后面的逻辑同服务器4成为小弟。
Server状态:选举状态
LOOKING,竞选状态。
FOLLOWING,随从状态,同步leader状态,参与投票。
OBSERVING,观察状态,同步leader状态,不参与投票。
LEADING,领导者状态。
选举消息内容
在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
服务器ID
数据ID
逻辑时钟
选举状态
节点类型:
1.持久节点(Persistent) : 客户端和服务器断开连接后,创建的节点不删除
2.短暂节点(Ephemeral): 客户端和服务器断开连接后,创建的节点自己删除
持久编号节点
短暂编号节点
Stat结构体:
czxid :创建节点的事务zxid
监听器原理:
1.首先要有一个main()线程
2.在main线程中创建zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connet)
一个负责监听(listener)
3.通过connet线程将注册的监听事件发送给Zookeeper
4.在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中
5.Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
6.listener线程内部调用了process()方法
写数据的流程:
1.客户端向zookeeper的server1上写数据,发送一个写请求
2.如果server1不是leader,那么server1会把接受到的请求进一步转发给
leader,因为每个zookeeper的server里面有一个leader。这个leader
会将写请求广播给各个server,比如server1和server2,各个server写
成功后就会通知leader。
3.当leader收到大多数server数据写成功了,那么久说明数据写成功了,
如果这里三个节点的话,只要有两个节点数据写成功了,那么就认为
数据写成功了,写成功之后,leader就会告诉server1数据写成功了。
4.server1会进一步通知client数据写成功了,这时就认为整个写操作成功了。
Zookeeper分布式安装部署:
1.把zookeeper的安装程序分别上传到指定的服务器上
2.解压安装程序,并重命名zookeeper
3.修改配置文件:
zoo.cfg
datadir = /opt/mysoft/zookeeper/zkData
添加集群的地址:
server.0=hadoop00:2888:3888
server.1=hadoop01:2888:3888
server.2=hadoop02:2888:3888
4.在zkData目录中添加myid文件,并在文件中添加服务器对应的数字
0
5.服务器的启动:
分别启动各个zookeeper服务器:
bin/zkServer.sh start
bin/zkServer.sh status
常用的shell命令:
启动客户端:bin/zkCli.sh
1.显示所有操作命令:help
2.查看当前znode中包含的内容:ls /
3.查看当前节点详细数据:ls2 /
4.分别创建2个普通节点: create /test 'guest'
create /admin 'manager'
5.创建短暂节点:create -e /sanguo/shuguo 'zhouyu'
6.创建带序号的节点:create -s /sanguo/wuguo 'liubei'
7.修改节点的值:set /sanguo/shuguo 'liubei'
8.监听节点的值:get /sanguo watch
在别的节点修改:set /sanguo 'zhangfei' , 节点会发生变化
提示:SyncConnected type:NodeDataChanged path:/sanguo
监听:注册一次有效一次
9.监听路径的变化:
ls /sanguo watch
在另外的节点:create /sanguo/wuguo 'sunquan'
Java API操作zookeeper框架:
1.创建maven项目
2.添加pom.xml依赖
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
zookeeper
zookeeper
0.0.1-SNAPSHOT
jar
zookeeper
http://maven.apache.org
UTF-8
junit
junit
4.12
test
org.apache.zookeeper
zookeeper
3.4.10
3.编写测试类
package com.zoo;
import java.util.List;import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.ZooDefs.Ids;
import org.junit.Before;
import org.junit.Test;/**
* Hello world!
*
*/
public class TestZookeeper {
String connect = "hadoop00:2181,hadoop01:2181,hadoop04:2181";
int sessionTimeout = 2000;
ZooKeeper zooKeeper = null;@Before
public void init() throws Exception {
zooKeeper = new ZooKeeper(connect, sessionTimeout, new Watcher() {
public void process(WatchedEvent event) {
// 这是监听程序,用于监听zookeeper操作变化
try {
Listchildren = zooKeeper.getChildren("/", true);
for(String child : children) {
System.out.println(child);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 创建一个节点
@Test
public void createNode() throws Exception, InterruptedException {
String path = zooKeeper.create("/sanguo/hanguo", "hanfeizi".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.print(path);
}
// 获取子节点 并监控节点的变化
@Test
public void getDataAndWatch() throws Exception {
Listchildren = zooKeeper.getChildren("/", true);
for(String child : children) {
System.out.println(child);
}
// 监听,让线程处于休眠状态, 然后在zookeeper客户端通过shell命令添加删除操作节点,在初始化的监听程序里打印信息
Thread.sleep(Long.MAX_VALUE);
}
// 判断节点是否存在
@Test
public void exist() throws Exception{
Stat stat = zooKeeper.exists("/sanguo", false);
System.out.println(stat == null ? "not exist":"exist");
}
}