-
协调大数据之间的框架合作
-
入门
-
Zookeeper为分布式应用提供协调服务的Apache项目 观察者模式
-
工作机制
-
存数据,通知注册过的观察者 ----协调
-
Zookeeper 特点 :
-
主从(leader-follower)
-
-
-
6)实时性,在一定时间范围内,Client能读到最新数据
-
-
-
数据结构
-
和unix不同,每个节点是ZNode 可以存数据也可以有子节点 ,可以是文件也可是文件夹
-
应用场景:HA,Kafka,HBase(只需要把zookeeper打开就可以了)
-
下载地址:zookeeper.apache.org 雅虎开发的zookeeper
-
-
-
安装
-
搭建Zookeeper
-
cd /opt/software/
-
传入zookeeper的包
-
tar -zxf zookeeper-3.4.10.tar.gz -C /opt/module/
-
cp zoo_sample.cfg zoo.cfg
-
bin/zkServer.sh start 完成单节点启动
-
-
配置zookeeper集群
-
bin/zkServer.sh stop
-
cd conf
-
vim zoo.cfg
-
dataDir=/opt/module/zookeeper-3.4.10/zkData
-
-
集群配置
-
加上这三行 server.2(id)=hadoop102(ip):2888(客户端连接端口):3888(选举端口号)
-
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
-
-
-
改完后cd ..
-
mkdir zkData
-
cd zkData/
-
touch myid
-
vim myid 写个2
-
-
cd ..
-
cd bin
-
vim zkEnv.sh
-
ZOO_LOG_DIR="/opt/module/zookeeper-3.4.10/logs"
-
配置java_Home
-
在开头写 export JAVA_HOME=/opt/module/jdk1.8.0_144
-
总结:
-
在zoo.cfg配置了zkData存储文件位置和集群信息
-
在conf中配置zkEnv 的日志和javahome
-
在zkData中新建myid文件
-
xsync zookeeper
-
分发完后修改myid 为对应id
-
-
-
启动
-
bin/zkServer.sh start
-
bin/zkServer.sh status 显示ERROR是因为zookeeper需要半数以上结点存活才可正常服务
-
-
-
实战(*!)
-
zookeeper命令行操作
-
连接zookeeper bin/zkCli.sh 不加端口号 默认连接本机的服务器
-
$ bin/zkCli.sh -server 127.0.0.1:2181
-
客户端命令行操作
-
-
命令基本语法 | 功能描述 |
help | 显示所有操作命令 |
ls path [watch] | 使用 ls 命令来查看当前znode中所包含的内容 |
ls2 path [watch] | 查看当前节点数据并能看到更新次数等数据 |
create | 普通创建 -s 含有序列 -e 临时(重启或者超时消失) |
get path [watch] | 获得节点的值 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
rmr | 递归删除节点 |
-
-
ls /zookeeper
-
注册
-
ls / watch
-
注册之后如果 /目录发生变化,zookeeper会通知 所有的观察者
-
-
创建结点
-
create /tt1234 abcdef
-
/tt1234 是结点名称, abcdef是结点之后存储的数据
-
-
参数 -s
-
创建一个带序号的结点 全局递增 文件名拼接序号
-
-
参数 -e
-
创建一个临时结点,在(会话结束)quit后临时结点会消失
-
如果是有序的临时结点
-
退出后,再创建有序结点,序号会自增 跳过临时序号
-
临时结点无法建立子节点
-
-
-
-
结点类型共四种
-
获取结点数据 get
-
get /ttt [watch]
-
在set后会出现监听事件(注册列表只会监听一次)
-
-
-
设置结点数据 set
-
set /ttt 2345
-
-
查看结点状态 stat /ttt
-
Stat结构体
-
-
1)czxid-创建节点的事务zxid
-
前4字节表示第几次启动,后8字节表示 第几次操作
-
每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。
-
事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
-
-
2)ctime - znode被创建的毫秒数(从1970年开始)
-
3)mzxid - znode最后更新的事务zxid
-
4)mtime - znode最后修改的毫秒数(从1970年开始)
-
5)pZxid-znode最后更新的子节点zxid
-
6)cversion - znode子节点变化号,znode子节点修改次数
-
7)dataversion - znode数据变化号
-
8)aclVersion - znode访问控制列表的变化号
-
9)ephemeralOwner- 如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
-
10)dataLength- znode的数据长度
-
11)numChildren - znode子节点数量
-
-
删除结点
-
delete 只能删除空结点(无子节点)
-
rmr /ttt 递归删除结点
-
-
-
内部原理
-
监听器原理
-
new Zookeeper时新建两个子线程(异步)
-
-
ZAB协议 zookeeper atomic broadcast
-
-
保证了数据顺序一致性
-
简单说就是两件事
-
没有leader选leader (术语:崩溃恢复)
-
有leader了就干活(术语:正常读写)
-
-
Paxos算法(扩展)
-
zookeeper借鉴了Paxos算法
-
-
-
选举机制
-
半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
-
Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
-
选举案例
-
上来先投自己
-
(1)服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是LOOKING状态。
-
(2)服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态。
-
(3)服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的Leader。
-
(4)服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了。
-
(5)服务器5启动,同4一样当小弟。
-
-
写数据流程【一致性,顺序性】
-
超过半数不同意则返回客户端不同意 ,同意的数据回滚
-
多数同意少数不同意,则不同意的server 重启 之后向leader或成功同步数据
-
这种情况的发生是因为网络原因,延迟收到待写队列中的数据,其他服务器已经将数据落实。
-
-
待写队列是按照操作编号顺序落实的,就算延迟收到,也不会乱序。
-
Observer (代表集群规模很大 / 集群跨数据中心)
-
比如有30000台服务器的集群
-
三台leader ,其余29997台Observer
-
正常运行的条件:是leader 存活半数以上
-
-
-
-
-
API
-
建立Maven 项目
-
加入log4j 和依赖 pom.xml
-
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</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.10</version>
</dependency>
</dependencies>
-
-
ZkClient.java
-
-
-
Watcher 是一个接口,匿名内部类中实现的方法是回调方法
-
-
ls ----》getChildren
-
卡住等待监听事件
-
可以在方法中自定义回调函数
-
-
create----》 create
-
ACL 权限控制 开放 ZooDefs.Ids.OPEN_ACL_UNSAFE
-
四种文件模式, 临时-e,持久,临时+顺序-e -s,持久+顺序 -s
-
get ---》getData
-
set --->setData
-
最好通过exists 的stat.getVersion
-
stat ---》exists 如果结点存在返回stat
-
delete
-
-
Zookeeper API实现 循环注册监听
-
API测试代码
import java.lang.String;
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 ZooKeeper zooKeeper;
private static final String SERVER="hadoop1002:2181,hadoop1003:2181,hadoop1004:2181";
private static final int SS_TIMEOUT=2000;
@Before
public void before() throws IOException {
zooKeeper=new ZooKeeper(SERVER, SS_TIMEOUT, new Watcher() {
//这是一个回调方法! 可以自定义
public void process(WatchedEvent watchedEvent) {
System.out.println("默认监听器");
}
});
}
@Test
public void ls () throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren("/", true);
System.out.println("=========================");
for(String s:children)
{
System.out.println(s);
}
System.out.println("=========================");
}
@Test
public void create() throws KeeperException, InterruptedException {
String s = zooKeeper.create("/IDEA", "stw".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println(s);
Thread.sleep(Long.MAX_VALUE);//因为创建的是临时结点,如果直接跳出结点会消失
}
@Test
public void get() throws KeeperException, InterruptedException {
Stat exists = zooKeeper.exists("/aaa", false);
if (exists != null) {
byte[] data = zooKeeper.getData("/aaa", false, exists);
System.out.println(new String(data));
}
else
System.out.println("文件不存在");
}
@Test
public void set() throws KeeperException, InterruptedException {
Stat exists = zooKeeper.exists("/aaa", false);
if (exists != null) {
zooKeeper.setData("/aaa", "stw.dadasdasd".getBytes(),exists.getVersion());
System.out.println(exists.getDataLength());
}
}
public void delete() throws KeeperException, InterruptedException {
Stat exists = zooKeeper.exists("/aaa", false);
if(exists!=null) {
zooKeeper.delete("/aaa", exists.getVersion());
System.out.println("delete completed");
}
else
System.out.println("要删除的结点不存在");
}
@Test
public void rmr() throws KeeperException, InterruptedException {
//rmr
ZKUtil.deleteRecursive(zooKeeper,"/aaa");
}
public void register() throws KeeperException, InterruptedException {
byte[] data = zooKeeper.getData("/aaa", new Watcher() {
public void process(WatchedEvent watchedEvent) {
try {
register();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, null);
System.out.println(new String(data));
}
@Test
public void testReg() {
try {
register();
Thread.sleep(Long.MAX_VALUE);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}