zookeep的安装
这里使用学校的云主机演示:
- 安装JDK
- 关闭防火墙
service iptables stop
chkconfig iptables off - 上课阶段,要求所有的框架都安装/home/software
cd /home/software/ - 下载Zookeeper安装包 - 云主机只能下载禁止上传
wget http://bj-yzjd.ufile.cn-north-02.ucloud.cn/zookeeper-3.4.8.tar.gz - 解压
tar -xvf zookeeper-3.4.8.tar.gz - 进入安装目录的子目录conf下
cd zookeeper-3.4.8/conf - 复制
cp zoo_sample.cfg zoo.cfg - 编辑
vim zoo.cfg
修改
dataDir=/home/software/zookeeper-3.4.8/tmp - 进入安装的子目录bin下
cd …/bin - 启动Zookeeper
sh zkServer.sh start - 通过jps查看出现了QuorumPeerMain表示Zookeeper启动了
- 查看状态
sh zkServer.sh status
如果出现Mode:standalone表示启动成功
如果启动失败,查看zookeeper.out,从这个文件中找出启动失败的原因
基本理论
每个节点上可以挂载不同类别的服务器。
启动客户端的命令:进入bin目录下,输入sh zkCli.sh
相关命令
节点属性/节点信息
一个节点刚创建的时候,cZxid/mZxid/pZxid三者是一致的
事务id是全局递增的,可以理解为是整个过程第几个写操作
节点类型
因为zookeeper中不支持同名文件,所以如果设置成添加顺序节点就表示再添加节点的时候会自动在节点名上添加一个随机值。
使用代码操作zookeeper
新建项目添加如下依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.1</version>
</dependency>
</dependencies>
使用代码连接zk,并创建删除添加节点
package cn.tedu.zookeeper;
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;
import java.util.concurrent.CountDownLatch;
public class ZookeeperDemo1 {
private ZooKeeper zk;
// 连接Zookeeper
@Before
public void connect() throws IOException, InterruptedException {
// connectString - 连接IP+端口
// sessionTimeout - 会话超时时间,单位默认为毫秒
// watcher - 监控者,用于监控连接是否建立
// Zookeeper底层是依靠Netty来实现连接过程
// Netty基于NIO来实现的异步非阻塞的通信框架
// Zookeeper发起连接,是一个单独的任务 - 单独的一个线程
// @Test表示会把当前的方法放到一个单独的测试线程类中执行
// 非阻塞:无论连上还连不上,都会继续往下执行
// 异步:Zookeeper可能还在连接过程中,测试线程就可以抢占资源往下执行
CountDownLatch cdl = new CountDownLatch(1);
zk = new ZooKeeper("192.168.64.12:2181", // 写自己的云主机IP
5000, // 会话超时时间为5s,现阶段这个值在4000~40000
event -> {//使用lambda表达式定义的一个方法,用于判断监听连接是否建立
// 判断连接是否建立
if (event.getState() == Watcher.Event.KeeperState.SyncConnected)
System.out.println("连接建立~~~");
cdl.countDown();
}
);
// 在上述连接过程建立完成之前,测试线程即使抢到了也需要阻塞
cdl.await();
System.out.println("finish");
}
// 创建节点
@Test
public void createNode() throws KeeperException, InterruptedException {
// String path - 节点路径
// data - 节点数据
// acl - 权限策略,这里使用的权限表示:创建,读取,删除都可以
// createMode - 节点类型
// 返回值表示节点的名字
String name = zk.create("/log", "log servers".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(name);
}
// 修改数据
@Test
public void setData() throws KeeperException, InterruptedException {
// 第三个参数version - 版本 -> dataVersion - 数据版本 因为每修改一次这里的版本就会增加1,
// 所以它存在的意义是防止线程并发产生误操作。在修改数据的时候,会校验这个version和dataVersion是否一致
// 如果不一致,则直接报错;只有版本一致才能修改
// 如果需要忽略版本校验,那么将version的值改为-1
// 返回值表示节点信息/属性
// Stat s = zk.setData("/log", "log management".getBytes(), -1);
zk.setData("/log", "log management".getBytes(), 3);
}
// 获取数据
@Test
public void getData() throws KeeperException, InterruptedException {
// 需要节点属性
// Stat s = new Stat();
// byte[] data = zk.getData("/log", null, s);
// 不需要节点属性
byte[] data = zk.getData("/log", null, null);
System.out.println(new String(data));
}
// 获取子节点
@Test
public void getChildren() throws KeeperException, InterruptedException {
// 将子节点的节点名放到一个List中返回
List<String> ps = zk.getChildren("/", null);
for (String p : ps) {
System.out.println(p);
}
}
// 删除节点
@Test
public void deleteNode() throws KeeperException, InterruptedException {
zk.delete("/log", -1);
}
// 判断节点是否存在
@Test
public void exist() throws KeeperException, InterruptedException {
// 如果节点存在,则返回节点信息
// 如果节点不存在,则返回null
Stat s = zk.exists("/log", null);
System.out.println(s);
}
}
监控节点,执行相关业务
package cn.tedu.zookeeper;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZookeeperDemo2 {
private ZooKeeper zk;
// 连接Zookeeper
@Before
public void connect() throws IOException, InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
zk = new ZooKeeper("10.9.162.133:2181", 5000,
e -> {
if (e.getState() == Watcher.Event.KeeperState.SyncConnected)
System.out.println("连接成功~~~");
cdl.countDown();
});
cdl.await();
}
// 监控节点的数据是否改变
@Test
public void dataChanged() throws KeeperException, InterruptedException {
// Zookeeper的所有的请求都是通过Netty完成的
// 这边还在监控过程中,另一边测试线程就可以抢占执行权执行
CountDownLatch cdl = new CountDownLatch(1);
zk.getData("/log", e -> {
if (e.getType() == Watcher.Event.EventType.NodeDataChanged)
// 实际开发中,实际上需要记录日志(IP,时间,原来的数据,新的数据等)
System.out.println("节点数据被改变了!!!");
cdl.countDown();
}, null);
// 在上面的线程监控到数据改变之前,当前的测试线程需要阻塞不能结束
cdl.await();
System.out.println("finish");
}
// 监控子节点个数变化
@Test
public void childrenChanged() throws KeeperException, InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
zk.getChildren("/log", e -> {
if (e.getType() == Watcher.Event.EventType.NodeChildrenChanged)
System.out.println("子节点个数发生改变!!!");
cdl.countDown();
});
cdl.await();
}
// 监控节点的增删
@Test
public void nodeChanged() throws KeeperException, InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
zk.exists("/news", e -> {
if (e.getType() == Watcher.Event.EventType.NodeCreated)
System.out.println("节点被创建!!!");
else if (e.getType() == Watcher.Event.EventType.NodeDeleted)
System.out.println("节点被删除!!!");
cdl.countDown();
});
cdl.await();
}
}
zk完全分布式的搭建
- 三台云主机安装JDK
- 三台云主机关闭防火墙
- 在第一个节点上解压Zookeeper安装包
tar -xvf zookeeper-3.4.8.tar.gz - 进入安装目录的子目录conf下
cd zookeeper-3.4.8/conf - 复制
cp zoo_sample.cfg zoo.cfg - 编辑
vim zoo.cfg
修改
dataDir=/home/software/zookeeper-3.4.8/tmp
在文件尾部添加
server.编号=IP:port1:port2
例如
server.1=10.9.162.133:2888:3888
server.2=10.9.152.65:2888:3888
server.3=10.9.130.83:2888:3888 - 在Zookeeper的安装目录下创建一个tmp
cd …
mkdir tmp - 进入tmp
cd tmp - 编辑
vim myid - 回到software目录下
cd /home/software/ - 远程拷贝
scp -r zookeeper-3.4.8 root@10.9.152.65:/home/software/
scp -r zookeeper-3.4.8 root@10.9.130.83:/home/software/
云主机密码:tarena2017Up; - 修改另外两个云主机的myid
cd /home/software/zookeeper-3.4.8/tmp
vim myid - 三台云主机进入bin目录启动Zookeeper
cd /home/software/zookeeper-3.4.8/bin
sh zkServer.sh start - 查看状态
sh zkServer.sh status
如果启动成功,那么应该出现1个leader+2个follower
选举机制
myid是在配置文件中配置的值
脑裂
在zk集群中,出现多个leader的情况称之为
产生原因:1.集群产生分裂 2.裂完之后还进行了选举
例如:集群中有三个节点分裂出去了,但是他们不会进行选举,因为节点个数不足一半,同时因为节点个不足一半,会停止对外服务。
产生两个leader的原因如下边:有leader的一半被分裂出去了,同时有leader的集群不足一半,那么剩余节点会进行选举,产生一个新的老大