一、认识Zookeeper
官网地址:https://zookeeper.apache.org/
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
Zookeeper=文件系统+通信机制 (工作机制:观察者模式)
特点:
1)Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群。
2)集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
3)全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
4)更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
5)数据更新原子性,一次数据更新要么成功,要么失败。
6)实时性,在一定时间范围内,Client能读到最新数据。
应用场景:
- 统一命名服务
- 统一配置管理
- 统一集群管理
- 服务器节点动态上下线
- 软负载均衡
选举机制:
- 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
- Zookeeper虽然在配置文件中并没有指定Leader和Follower。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
(1)服务器1启动,发起一次选举。服务器1投自己一票,此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息,此时服务器1发现服务器2的ID比自己目前投票推举的(服务器1)大,则更改选票为推举服务器2,此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING;
(3)服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3,此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票,此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息,交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
(5)服务器5启动,于4的过程一样,更改选票信息为服务器3,并更改状态为FOLLOWING。
小结:投票选举的过程与mtime-znode最后修改时间和配置id有关,先会找最后修改的Server当Leader,若一样,则会找配置id大的。
节点类型:
监听器原理:
写数据流程:
二、本地模式安装(以windows系统为例,与linux安装相同)
1、确保已经安装JDK后,从官网下载安装包,解压到指定目录。
2、配置修改。
将conf路径下的zoo_sample.cfg修改为zoo.cfg
打开zoo.cfg文件,修改dataDir路径。(路径可以自己设置,方便查看,别忘记创建设置的文件夹)
dataDir=../data
3、操作Zookeeper
windows直接执行bin目录下的zkServer.cmd启动服务端,执行zkCli.cmd启动客户端
linux
启动服务端:bin/zkServer.sh start
查看启动状态:bin/zkServer.sh status
启动客户端:bin/zkCli.sh
三、分布式安装(以Linux为例)
1、从官网下载压缩包,解压到指定目录下。
tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
2、同步内容到其他服务器。
xsync zookeeper-3.4.10/
3、配置服务器编号。
扫描二维码关注公众号,回复:
16765674 查看本文章
在/opt/module/zookeeper-3.4.10/目录下创建zkData:
mkdir -p zkData
在zkData目录下创建一个myid文件:
touch myid
vi myid
添加与server对应的编号:2
拷贝配置好的zookeeper到其他机器上(注意修改编号,确保唯一):
xsync myid
4、配置zoo.cfg文件
mv zoo_sample.cfg zoo.cfg
vi zoo.cfg
修改数据存储路径配置:
dataDir=/opt/module/zookeeper-3.4.10/zkData
增加如下配置:
#####################cluster############################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
server.(几号服务器)= (服务器ip地址):(服务器与集群中的Leader服务器交换信息的端口):(选举时服务器相互通信的端口)
四、shell命令操作
(1)显示所有操作命令。
help
(2)查看当前znode中所包含的内容。
ls /
(3)查看当前节点详细数据。
ls2 /
(4)创建普通节点。
create /ceshi "ceshi"
(5)获得节点的值。
get /ceshi
(6)创建短暂节点。
create -e /ceshi "ceshi"
(7)创建带序号的节点。(如果原来没有序号节点,序号从0开始依次递增。如果原节点下已有2个节点,则再排序时从2开始,以此类推)
create -s /ceshi "ceshi"
(8)修改节点数据值。
set /ceshi "ceshi"
(9)节点的值变化监听。
get /ceshi watch
(10)节点的子节点变化监听(路径变化)。
ls /ceshi watch
(11)删除节点。
delete /ceshi
(12)递归删除节点。
rmr /ceshi
(13)查看节点状态。
stat /ceshi
五、代码实现
1、引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>
</dependencies>
2、添加日志配置文件
在项目resources目录下新建log4j.properties文件。
log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.conversionPattern=%d %p [%c] - %m%n
3、编写ZookeeperTest测试类
package com.yzs;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class ZookeeperTest {
private String connectString = "127.0.0.1:2181";
private int sessionTimeout = 2000;
private ZooKeeper zooKeeper;
//连接zookeeper服务端
@Before
public void connectZookeeper() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
List<String> children = null;
try {
children = 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 KeeperException, InterruptedException {
zooKeeper.create("/sanguo","sanguo".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
//获取子节点并监听数据变化
@Test
public void getChildrenAndWatch() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren("/", true);
for (String child : children){
System.out.println(child);
}
//延时阻塞
Thread.sleep(Long.MAX_VALUE);
}
//判断是否存在节点
@Test
public void exist() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists("/sanguo", false);
System.out.println(stat == null?"not exist":"exist");
}
//获取节点上的数据
@Test
public void getNode() throws KeeperException, InterruptedException {
byte[] data = zooKeeper.getData("/sanguo", false, null);
System.out.println(new String(data));
}
}