Zookeeper(CP)
以集群的方式【leader和follower】为分布式应用提供协调服务、负责存储和管理大家都关系的数据,接受观察者注册、消息分发等服务
特点:
-
只要有半数以上的节点存活就能保证zookeeper的集群正常工作
-
每一个Client保存一份相同的数据副本
-
来自同一个client的跟新请求可以顺序执行
-
数据更新原子性,且在一定时间范围内,client能读到最新的数据
-
软负载均衡:可以让访问数最少的服务器去处理最新服务器请求
启动命令
#启动zk服务器,查询zk服务器状态
./zkServer.sh status
./zkServer.sh start path/to/zoo.config
./zkServer.sh stop
#连接服务器
./zkCli.sh
客户端命令行
create treeNode data #在某个节点下存放数据
create -s treeNode data #有序号的节点,记录节点创建的先后顺序,没有从零开始
ls TreeNode # 获取某个树节点下的所有数据
delete TreeNode #删除节点,如果下面还有子节点是无法删除的
rmr Tree #删除某个子树
get treeNode #获取某个节点下的数据
get treeNode watch #注册对某个节点修改的监听,只会有效一次
set treeNode data #改变值
get path #获取某个树形结构下的值
get -w path #获取某个树形结构下的值,并监听一次变化
helo #帮助手册
quit#退出
短暂数据:
create -e treeNode data #在某个节点下存放数据,当前客户端与服务器断开则节点消失
节点参数:
czxid:创建该节点的事务ID、mzxid:最后更新的事务ID、cversion:节点的修改次数、dataversion:数据变化号
节点类型【树形结构】
持久节点,顺序【名字递增】&非顺序:persistent、persistent_sequential,除了在内存中,在磁盘中也会保存一份
临时节点,顺序&非顺序:ephemeral、ephemeral_sequential 短暂节点在与客户端断开连接后,创建的节点自己删除了。其原理是通过ping去保活,如果断开,则会发生session-timeOut,顺序的临时节点适合用来充当分布式锁。相比redis集群而言,zookeeper集群具备更好的数据一致性。另外临时节点不允许创建子节点
容器节点:当没有子节点时,会被服务器删除【默认60s检查一次】
TTL节点:过了TTL指定节点的时间就会被服务器删除,和redis一致
集群搭建
在.cfg文件中设置存储数据的目录,同时在目录下touch一个myid,并设置一个唯一标识
配置文件:默认是/conf/zoo.cfg
server.A1=B:C:D
server.A2=B:C:D
server.A3=B:C:D
#Ai:myid,唯一的数字标识,在数据存储目录中myid文件里定义
#B:服务器的IP地址
#C:leader服务器交换信息的端口号
#D:传递选举信息的端口号
当半数启动后集群开始正常运转【一般三个或以上】
节点的读写
在zookeeper中,某个节点接受到客户端写请求,会将请求转发给leader,由leader广播写请求,并统计成功数。当超过半数回复成功时,才会向客户端返回成功
CAP:
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance):将数据项复制到多个节点上,当某些节点发生故障,仍然可以访问到数据的能力
整合spring
连接并设置监听是否变化,产生变化后再次监听:不依赖spring,只依赖一个zookeeper.jar包,如果是Spring框架,可以通过配置pean的方式,直接注入 zookeeper
public class ZooKeeperTest {
private int sessionTimeout = 2000;
private String connectString="222.201.144.247:2181";
public ZooKeeper zooKeeper;
@Test
public void init() throws IOException, KeeperException, InterruptedException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
System.out.println("_________________start___________________");
List<String> list = zooKeeper.getChildren("/",true);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// String s=zooKeeper.create("/connect", "spring".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.getChildren("/",true);//设置监听,只会生效一次
Thread.sleep(Long.MAX_VALUE);
}
}
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
监听服务器上下线:
服务器集群向Zookeeper集群上传访问量总量,且数据类型为ephemeral,服务器掉线则数据消失
客户端集群向Zookeeper监听数据getChildren(),服务器宕机、上线时做出处理并重设监听
//客户机,主机部分向Zookeeper集群里的/下写上带序号、ephemeral的key 和 hostName即可,而由客户机负责监听
public class DistributeClient {
public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
DistributeClient client = new DistributeClient();
//连接到Zookeeper集群
client.getConnect();
//设置监听在控制台打印节点
client.getChildren();
//业务部分
client.business();
}
private int sessionTimeout = 2000;
private String connectString="222.201.144.247:2181";//如果是Zookeeper集群的话直接在用逗号隔开即可
public ZooKeeper zooKeeper;
private void getConnect() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getChildren();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
private void getChildren() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren("/",true);
ArrayList<String> host = new ArrayList<>();
for (String child :children){
byte[] data = zooKeeper.getData("/"+child,false,null);
host.add(new String(data));
}
System.out.println(host);
}
private void business(){
}
}
利用zookeeper实现分布式锁
方法一:
获取锁:create -e /ephemeral/lock threadName 创建失败则监听 get -w /ephemeral/lock【此时处于阻塞状态】,利用Watcher重新获取锁。弊端:所有的线程都需要反复的监听,尝试获取锁,产生系统资源浪费
方法二:【只能实现公平锁】
请求直接在 create -e -s /ephemeral/lock threadName 创建一个临时顺序节点,并判断自己是否是最小的节点,如果是,则获取锁,若不是,则只监听前一个节点【只比其小一的节点】并等待。最后通过delete释放锁
posted on
浙公网安备 33010602011771号