目录
推荐官网:https://zookeeper.apache.org 就是全英文......
zookeeper:
zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
Zookeeper集群是由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。客户端可以和集群中的任一Server建立连接,当读请求时,所有Server都可以直接返回结果;当请求为数据变更请求时,Follower会将请求转发给Leader节点,Leader节点接收到数据变更请求后,首先会将变更写入本地磁盘,当持久化完毕后才会将变更写入内存,并将变更后的数据同步到各个Follower。
Zookeeper 集群中的机器分为以下三种角色:
- Leader :为客户端提供读写服务,并维护集群状态,它是由集群选举所产生的;
- Follower :为客户端提供读写服务,并定期向 Leader 汇报自己的节点状态。同时也参与写操作“过半写成功”的策略和 Leader 的选举;
- Observer :为客户端提供读写服务,并定期向 Leader 汇报自己的节点状态,但不参与写操作“过半写成功”的策略和 Leader 的选举,因此 Observer 可以在不影响写性能的情况下提升集群的读性能。
zookeeper目录结构
zookeeper 是一个目录树结构 :node可以存储数据1MB,节点分为 持久节点,临时节点(session级别),序列节点。
zookeeper特征:
- 顺序一致性:客户端的更新将按发送顺序应用。
- 原子行:更新成功或失败,没有部分结果。
- 统一视图:客户端将会看到服务端相同的视图,不管它连的是那一个服务端。
- 可靠性:一旦更新成功,它将一直持续到被下一个客户端更新覆盖。
- 及时性:保证客户对系统的看法在一定时间内更新。
安装
安装jdk,并设置javahome 去oracle官网或者云
下载zookeeper 官方网站
tar xf apache-zookeeper-3.6.3-bin
mkdir /opt/ aaa
mv zookeeper /opt/aaa
vi /etc/profile
export ZOOKEEPER_HOME=/opt/aaa/apache-zookeeper-3.6.3-bin
export PATH=$PATH:$ZOOKEEPER_HOME/bin
cd zookeeper/conf
cp zoo.sem*.cfg zoo.cfg
vi zoo.cfg
dataDir= /var/aaaa
server.1=node01:2888:3888 //几台自己设置
mkdir -p /var/aaaa/zk datadir给的路径
echo 1 > /var/aaaa/zk/myid
cd /opt scp -r ./文件夹名/ node02:pwd
zkServer.sh 启动
zkCli.sh 连接
基本操作:
ls/ //显示目录结构
[zk: localhost:2181(CONNECTED) 2] create /xl
Created /xl //创建 节点
[zk: localhost:2181(CONNECTED) 3] ls /
[xl, zookeeper]
[zk: localhost:2181(CONNECTED) 6] create /xl/lx
Created /xl/lx
[zk: localhost:2181(CONNECTED) 7] ls /xl
[lx]
[zk: localhost:2181(CONNECTED) 8] get /xl
null //查看数据
[zk: localhost:2181(CONNECTED) 9] set /xl "hello"//存放数据 只能放1M 也是二进制安全的
[zk: localhost:2181(CONNECTED) 10] get /xl
hello
[zk: localhost:2181(CONNECTED) 13] get -s /xl //获取详细信息
hello
cZxid = 0x200000002 //64位 事务ID 创建 /xl的时候
//前32位 表示 leader的 纪元 第几个leader 后32位 事务递增序列 01234567.
//开启新的纪元 事务ID 从零开始
ctime = Wed Sep 01 12:13:55 CST 2021 //创建时间
mZxid = 0x200000005 //修改节点的事务ID
mtime = Wed Sep 01 12:16:36 CST 2021 //修改时间
pZxid = 0x200000004 //当前节点下创建的最后那个节点号
cversion = 1 //子节点更改次数
dataVersion = 1 //节点数据更改次数
aclVersion = 0 //节点ACL的更改次数
ephemeralOwner = 0x0 //持有者0x0相当于直接存在 持久化
//create -e 创建临时节点 临时节点 是有会话期的 当前会话结束 节点清除
//session 也是要统一视图 消耗事务ID的
dataLength = 5 //数据内容长度
numChildren = 1 //数据节点当前的子节点个数
[zk: localhost:2181(CONNECTED) 14] create /abc "" //创建 abc节点 其他在abc下操作
Created /abc
[zk: localhost:2181(CONNECTED) 15] ls /
[abc, xl, zookeeper]
[zk: localhost:2181(CONNECTED) 16] create -s /abc/xxl "xiongdi" //序列
Created /abc/xxl0000000000 //帮你做 区分 隔离
[zk: localhost:2181(CONNECTED) 1] create -s /abc/xxl "laoer" //在开个客户端 序列递增
Created /abc/xxl0000000002 //分布式下 统一命名
[zk: localhost:2181(CONNECTED) 2] ls /abc //abc 下有两个节点
[xxl0000000001, xxl0000000000]
[zk: localhost:2181(CONNECTED) 26] delete /abc/xxl0000000000
//删除操作
[zk: localhost:2181(CONNECTED) 27] delete /abc/xxl0000000001
[zk: localhost:2181(CONNECTED) 28] ls /abc
[]
[zk: localhost:2181(CONNECTED) 29] create -s /abc/aaa
Created /abc/aaa0000000002 //在创建 2
Watch 监听
Zookeeper 中一个常用的功能是 Watcher(事件监听器),它允许用户在指定节点上针对感兴趣的事件注册监听,当事件发生时,监听器会被触发,并将事件信息推送到客户端。该机制是 Zookeeper 实现分布式协调服务的重要特性。
ZAB协议
paxos :基于消息传递的一致性算法 前提是没有拜占庭将军问题//可以去查一查看一下
zab属于paxos 的简单版
ZAB 协议是 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议。通过该协议,Zookeeper 基于主从模式的系统架构来保持集群中各个副本之间数据的一致性。
所有的事务请求必须由唯一的 Leader 服务来处理,Leader 服务将事务请求转换为事务 Proposal,并将该 Proposal 分发给集群中所有的 Follower 服务。如果有半数的 Follower 服务进行了正确的反馈,那么 Leader 就会再次向所有的 Follower 发出 Commit 消息,要求将前一个 Proposal 进行提交。 过半
消息广播
ZAB 协议的消息广播过程使用的是原子广播协议。在整个消息的广播过程中,Leader 服务器会每个事物请求生成对应的 Proposal,并为其分配一个全局唯一的递增的事务 ID(ZXID),之后再对其进行广播。
崩溃恢复
当整个服务框架在启动过程中,或者当 Leader 服务器出现异常时,ZAB 协议就会进入恢复模式,通过过半选举机制产生新的 Leader,之后其他机器将从新的 Leader 上同步状态,当有过半机器完成状态同步后,就退出恢复模式,进入消息广播模式。
API代码简单实现
//工具类
public class ZKUtils {
private static ZooKeeper zk; //创建个统一目录
private static String address = " 你要连接的,你要链接的/testConf";
private static DefaultWatch watch = new DefaultWatch();
private static CountDownLatch cdl = new CountDownLatch(1);
public static ZooKeeper getZK() {
try {
zk = new ZooKeeper(address,1000,watch);
watch.setCc(cdl);
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return zk;
}
}
//接收数据的
public class Myconf {
//类型 不止String 只拿String 举例了
private String conf;
public String getConf() {
return conf;
}
public void setConf(String conf) {
this.conf = conf;
}
}
public class WatcherCallBack implements Watcher, AsyncCallback.StatCallback,
AsyncCallback.DataCallback {
ZooKeeper zk;
Myconf myconf;
CountDownLatch cc = new CountDownLatch(1);
public Myconf getMyconf() {
return myconf;
}
public void setMyconf(Myconf myconf) {
this.myconf = myconf;
}
public ZooKeeper getZk() {
return zk;
}
public void setZk(ZooKeeper zk) {
this.zk = zk;
}
//取数据
public void aWait(){
zk.exists("/Appconf",this ,this,"ABC");
try {
cc.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
if (data != null) {//真正取到数据
String s = new String(data);
myconf.setConf(s);
cc.countDown();
}
}
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
if (stat != null) {//代表节点已经存在了
//取 数据
zk.getData("/Appconf", this, this, "sdf");
}
}
@Override
public void process(WatchedEvent event) {
switch (event.getType()) {
case None:
break;
case NodeCreated:
zk.getData("/Appconf", this, this, "sdf");
break;
case NodeDeleted://节点被删除
myconf.setConf("");//删除就被清空了
cc = new CountDownLatch(1); //重新赋值
break;
case NodeDataChanged://如果节点的数据被变更了怎么办 取数据在执行一变
zk.getData("/Appconf", this, this, "sdf");
break;
case NodeChildrenChanged:
break;
case DataWatchRemoved:
break;
case ChildWatchRemoved:
break;
case PersistentWatchRemoved:
break;
}
}
}
public class TestConfig {
ZooKeeper zk;
@Before
public void conn() {
zk = ZKUtils.getZK();
}
@After
public void close() {
try {
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void getConf() {
WatcherCallBack watcherCallBack = new WatcherCallBack();
watcherCallBack.setZk(zk);
Myconf myconf = new Myconf();
watcherCallBack.setMyconf(myconf);
//1.节点不存在 从没有到创建 2.节点存在的时候 存在到修改数据
watcherCallBack.aWait();
while (true) {//要么打印 要么等待数据
if ("".equals(myconf.getConf())) {
System.out.println("deng.........");
//阻塞一下
watcherCallBack.aWait();
} else {
System.out.println(myconf.getConf());
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}