前言
传统系统的问题:
- 系统中的单点的风险,单点崩溃,整个系统就不能用了。
- 传统单点解决方案:利用协调软件进行双击热备。
- 传统主备协调方案的认识:
- 协调软件本身不是分布式的,只能配置一个。
- 只能一个主一个备
初识Zookeeper
1.概述
1.1 Zookeeper定义
Zookeeper是google的Chubby的一个开源实现
Zookeeper:是Apache组织下的高性能,分布式的应用协调服务框架
Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应,从而实现集群中类似Master/Slave管理模式
Zookeeper=文件系统+监视通知机制
1.2 特点
1)最终一致性
2)原子
3)可靠
4)实时
5)顺序
6)单一视图
1.3 Zookeeper主备协调的实现
- 1)通过Zookeeper提供的两个重要特性
- 2)主备协调利用上面提到的临时ZNode与通知
- 3)支持Zookeeper协调的灵魂
1.3.1 Zookeeper提供的两个重要特性
- 特性1:文件系统
层级树形结构
每个节点称作ZNode
每个节点由/分割
什么是Znode呢?
定义:Znode是Zookeeper存储数据的数据单元。
Znode用来组织存储数据,它有3种类型。
- 持久ZNode:在Zookeeper中创建的Znode是永久存在的。
- 临时ZNode:客户端断开即删除的Znode。
- 顺序ZNode:是指为避免创建的Znode名字冲突而在名字后追加10位顺序数字的Znode。
ZooKeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个ZNode。
很显然zookeeper集群自身维护了一套数据结构。这个存储结构是一个树形结构,其上的每一个节点,我们称之为”znode”,每一个znode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识
- 特性2:通知(监视)
Watcher
定义:watcher是仿效观察者模式实现的Zookeeper的事件通知机制。
监听的内容:
1)ZNode数据变化
2)Znode删除事件
3)子目录节点增加删除事件
1.3.2 主备协调利用上面提到的临时ZNode与通知
1.3.3支持Zookeeper协调的灵魂
Zookeeper的协议
- Paxos协议
是什么呢?
一个基于消息传递的一致性算法
基于Paxos实现的Zookeeper特点:
1)最终一致性
是指分布式系统中数据的多个副本在经过一段时间后,最终会达到一致状态的特性。
2)原子
是指更新数据只能成功或者失败,没有中间状态的特性
3)可靠
是指在ZooKeeper中,如果消息被一台服务器接受,那么它将被所有服务器接受的特性。
4)实时
是指Zookeeper能保证客户端得到刚更新的数据的特性。
5)顺序
是指在ZooKeeper中同一个消息发布顺序一致的特性
6)单一视图
基于Paxos实现的Zookeeper角色:
领导者(Leader):是负责进行投票的发起和决议,更新系统状态的Zookeeper角色
学习者(Learner):
跟随者Follower:是接收客户请求并向客户端返回结果且在选主过程中参与投票的Zookeeper角色。
观察者Observer:是接受客户端连接,将写请求转发给Leader节点,不参与投票且同步Leader状态的Zookeeper角色。
客户端(Client):请求发起方
- zab协议:
广播模式–选举出来leader,对外提供服务
恢复模式—选举leader,也就是4大状态存在 - 原子广播:
通过投票确定某一件事能不能做(过半原则—zookeeper最好是奇数)
1.提交请求到follower,follower会将请求发送给leader
2.leader将请求下发给所有的follower,F来判断,并将结果返回给L
3.L将请求写入的文件分发到所有的follower------(最终一致性–及时在写入的过程中有节点连接不上,当恢复的时候还会把节点上写入数据) - 选举机制:
id----serverid(设置的myid)
zxid-----(股份)
逻辑时钟------每个一个周期回加1(根据逻辑时钟判断我当前选举的状态)
2. Zookeeper安装与命令操作
2.1 部署Zookeeper服务器的数量要求
1)小于等于255个节点
2)奇数节点
2.2 Zookeeper3种安装模式
1)单级模式
2)伪分布模式
3)安全分布模式
**2.3 Zookeeper操作**
1)命令操作
2)程序操作
2.3.1.1命令操作
-
默认启动命令客户端—zkCli.sh
-
启动连接指定的Zookeepeer----zkCli.sh -server ip:port
-
Zookeeper命令:
Zookeeper状态:
zkServer.sh status
显示指定路径下的内容:
ls 路径
创建临时节点,用quit命令退出或结束,会话节点消失:
create -e /name1 zhangsan1
创建顺序节点:
create -s /name2 zhangsan2
创建临时顺序节点:
creae -e -s /name3 zhangsan3
通过名字获取节点:
get /name2
删除指定名字的节点:
delete("/name1",-1);
修改节点内容:
set /name1 lisi
2.3.2.1 通过eclipse创建连接
Eclipse开发环境
通过指定端口与ip创建Zookeeper客户端对象
ZooKeeper client = new ZooKeeper("172.16.245.5:2181",1000, null);
创建节点,/name
String create = client.create("/name", "zhangsan".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
创建子节点,/name/sex
public class TestZ {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper client = new ZooKeeper("172.16.245.5:2181"/*链接字符串*/, 1000, null);
byte[] data = client.getData(“/name/sex”/*指定节点*/, true, null);
String db = new String(data);//将获取的数据转换为字符串
System.out.println(db);//打印字符串
}
}
获取节点内容,“/name/sex”的内容
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class TestZ {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper client = new ZooKeeper("172.16.245.5:2181"/*链接字符串*/, 1000, null);
byte[] data = client.getData(“/name/sex”/*指定节点*/, true, null);
String db = new String(data);//将获取的数据转换为字符串
System.out.println(db);//打印字符串
}
}
删除指定节点,“/name1111”
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class TestZ {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper client = new ZooKeeper("172.16.245.5:2181"/*链接字符串*/, 1000, null);
client.delete("/name1111", -1);
}
}
设定指定节点内容,“/namewjf”的内容为22222
import java.io.IOException;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
public class Testset {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
// TODO Auto-generated method stub
ZooKeeper zk = new ZooKeeper("172.16.245.5:2181", 1000, null);
zk.setData("/namewjf", "222222".getBytes(), zk.exists("/namewjf", true).getVersion()/*znode当前版本*/);
}
}
获取子节点
import java.io.IOException;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
public class testgetchild {public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper client = new ZooKeeper("172.16.245.5:2181"/*链接字符串*/, 1000, null);
List<String> list = client.getChildren("/name", true);
for(String str:list){
System.out.println(str);
}
}
}
设置监听机制:(当的znode发生变化的时候,监听一下,但是只能监听一次)
1、设置 setData getChildren
2、启动 delete exits create、getData
监听指定节点/name
public class TestWatcher implements Watcher{
//watcher只有在长链接的情况下才起作用,而且只能监听一次。
private static final int SESSION_TIMEOUT=1000;
private ZooKeeper zk = null;
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper client = new ZooKeeper("172.16.245.5:2181"/*链接字符串*/, 1000, null);
byte[] bs = client.getData("/name", new TestWatcher(), null);
System.out.println(new String(bs));
Thread.sleep(Long.MAX_VALUE);
}
//监听到指定的目录有变化,会触发此方法
@Override
public void process(WatchedEvent event) {
System.out.println(event.getPath());
}
}