是apache Hadoop项目下的子项目。是一个树形目录服务。是一个分布式的,开源的分布式应用程序的协调服务。简称zk。
主要功能:
https://blog.csdn.net/lingbo229/article/details/81052078
树形的目录服务,数据模型和unix的文件系统书目录类似,拥有一个层次化结构。
每一个节点被称为:ZNode,每个节点都会保存自己的数据和节点信息。
节点可以拥有子节点,同时允许少量(1mb)数据存储在该节点下。
Znode结构:
zk中的znode,包含四个部分:
节点分类:
数据持久化
zk的数据是运行在内存中的,必然会提供持久化机制,zk提供的持久化有2种:
同redis一样,采用混合方式进行恢复,先用快照恢复,然后用事物日志增量恢复。
zkServer.sh start
zkServer.sh status
zkServer.sh stop
zkServer.sh sretart
zkCli.sh [-server ip:host]
quit
ls 目录
ls -s 目录
create [-参数] 目录 [数据]
get 目录
set 目录 数据
delete 目录
目录下有子节点则不能删除deleteall 目录
help
Curator 介绍:
Netfix研发,捐献给apache。简化zookeeper客户端的使用
常用操作:
建立连接
public void testCurator(){
RetryPolicy retryPolicy=new ExponentialBackoffRetry(300,10);
// client= CuratorFrameworkFactory.newClient("192.168.224.135:2181", 60 * 1000, 15 * 1000, retryPolicy);
client= CuratorFrameworkFactory.builder()
.connectString("192.168.224.135:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.retryPolicy(retryPolicy)
.build();
client.start();
}
添加节点
public void testCreate() throws Exception {
//创建节点,没有指定数据,则默认数据为当前客户端的ip
String path= client.create().forPath("/app1");
System.out.println(path);
//指定数据
path= client.create().forPath("/app2","hehe".getBytes());
System.out.println(path);
//指定类型(零时,持久等等)
path= client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
System.out.println(path);
//创建多级节点
path= client.create().creatingParentsIfNeeded().forPath("/app4/p1");
System.out.println(path);
}
删除节点
public void testDel() throws Exception {
//删除单个节点
client.delete().forPath("/app1");
//删除节点下的所有节点
client.delete().deletingChildrenIfNeeded().forPath("/app4");
//必须删除成功,为了防止网络抖动,本质就是重试
client.delete().guaranteed().forPath("/app2");
//回调
client.delete().guaranteed().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
System.out.println("删除成功");
System.out.println(curatorEvent);
}
}).forPath("/app3");
}
修改节点
public void testSet() throws Exception {
//修改数据
client.setData().forPath("/app1","adff".getBytes());
//根据版本号修改
Stat status=new Stat();
client.getData().storingStatIn(status).forPath("/app1");
int version=status.getVersion();//目的是为了让其他客户端或者线程不干扰我
client.setData().withVersion(version).forPath("/app1","dxxd".getBytes());
}
查询节点
public void testGet() throws Exception {
//查数据
byte[] data=client.getData().forPath("/app1");
System.out.println(new String(data));
//查子节点
List<String> path=client.getChildren().forPath("/");
System.out.println(path);
//查状态
Stat status=new Stat();
client.getData().storingStatIn(status).forPath("/app1");
System.out.println(status);
}
Watch事件监听
分布式锁实现
单机时,并发时候,使用synchronized或者lock的方式来解决多线程间的代码同步问题。多线程运行在一个JVM下方,没有任何问题
分布式集群工作情况下,属于多JVM工作环境,无法通过多线程的锁解决问题。需要一种更加高级的锁机制,来处理 跨机器的进程之间的数据同步问题——这就是分布式锁
分类:
基于缓存实现
redis(不可靠),Memcache
基于Zookeeper实现
curator
基于数据库实现
乐观锁,悲观锁
zookeeper实现分布式锁的原理
核心思想:客户端获取锁,则创建节点,使用完锁,则删除节点
curator实现分布式锁APi
在curator里有五种锁方案:
测试代码
public class TicketTest {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket,"携程");
Thread t2=new Thread(ticket,"飞猪");
t1.start();
t2.start();
}
}
class Ticket implements Runnable{
private int t=10;
private InterProcessLock lock;
public Ticket(){
RetryPolicy retryPolicy=new ExponentialBackoffRetry(300,10);
CuratorFramework client= CuratorFrameworkFactory.builder()
.connectString("192.168.224.136:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.retryPolicy(retryPolicy)
.build();
client.start();
lock=new InterProcessMutex(client,"/lock");
}
@Override
public void run() {
while (true) {
try {
lock.acquire(3, TimeUnit.SECONDS);
if (t>0){
System.out.println(Thread.currentThread()+":"+t);
t--;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
如果使用上述的上锁方式,只要节点发生变化,就会出发其他节点的监听事件,这样的话对zk的压力很大,也就是羊群效应,使用链式监听可以解决这个问题。
我们可以把watch理解成是注册在特定Znode上的触发器。当这个Znode改变了,也就是调用create、delete、setData方法的时候,就会将触发Znode上注册的对应事件,请求watch的客户端就会接收到异步通知。客户端使用NIOfang s
交互如下:
使用ls -w 监听目录变化。get -w 是监听节点内容的。-R 递归监听。
serverid:服务器id
比如有三台服务器,编号分别为1,2,3.
编号越大,在选择算法中权重越大
Zxid:数据id
服务器中存放的最大数据ID值越大说明数据越新,在选举算法中权重越大
在Leader选举过程中,如果某台Zookeeper获得半数以上选票,则此zookeeper就可以成为Leader了。
集群节点挂掉只剩一台运行,则会休眠,不出现leader
集群以ip区分,伪集群以端口区分
准备工作:
安装jdk
上传zookeeper包
解压目录为
/usr/local/zk/zookeeper-1
/usr/local/zk/zookeeper-2
/usr/local/zk/zookeeper-3
创建data目录,并将conf下zoo_zample.cfg改为zoo.cfg
mkdir /usr/local/zk/zookeeper-1/data
mkdir /usr/local/zk/zookeeper-2/data
mkdir /usr/local/zk/zookeeper-3/data
mv /usr/local/zk/zookeeper-1/conf/zoo_sample.cfg /usr/local/zk/zookeeper-1/conf/zoo.cfg
mv /usr/local/zk/zookeeper-2/conf/zoo_sample.cfg /usr/local/zk/zookeeper-2/conf/zoo.cfg
mv /usr/local/zk/zookeeper-3/conf/zoo_sample.cfg /usr/local/zk/zookeeper-3/conf/zoo.cfg
配置zookeeper的dataDir和clientPort分别为 2181,2182,2183
修改/usr/local/zk/zookeeper-1/conf/zoo.cfg
vim /usr/local/zk/zookeeper-1/conf/zoo.cfg
clientPort=2181
dataDir=/usr/local/zk/zookeeper-1/data
修改2
vim /usr/local/zk/zookeeper-2/conf/zoo.cfg
clientPort=2182
dataDir=/usr/local/zk/zookeeper-2/data
修改3
vim /usr/local/zk/zookeeper-3/conf/zoo.cfg
clientPort=2183
dataDir=/usr/local/zk/zookeeper-3/data
配置集群:
在每个zookeeper的data目录创建一个myid文件,内容为1,2,3,这三各记录服务器id
echo 1 >/usr/local/zk/zookeeper-1/data/myid
echo 2 >/usr/local/zk/zookeeper-2/data/myid
echo 3 >/usr/local/zk/zookeeper-3/data/myid
在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和服务器集群ip列表
vim /usr/local/zk/zookeeper-1/conf/zoo.cfg
server.1=192.168.224.136:2881:3881
server.2=192.168.224.136:2882:3882
server.3=192.168.224.136:2883:3883
vim /usr/local/zk/zookeeper-2/conf/zoo.cfg
server.1=192.168.224.136:2881:3881
server.2=192.168.224.136:2882:3882
server.3=192.168.224.136:2883:3883
vim /usr/local/zk/zookeeper-3/conf/zoo.cfg
server.1=192.168.224.136:2881:3881
server.2=192.168.224.136:2882:3882
server.3=192.168.224.136:2883:3883
server.服务器ID=服务器Ip地址:服务器之间通信端口:服务器之间投票选举端口
启动集群:
/usr/local/zk/zookeeper-1/bin/zkServer.sh start
/usr/local/zk/zookeeper-2/bin/zkServer.sh start
/usr/local/zk/zookeeper-3/bin/zkServer.sh start
查看状态
/usr/local/zk/zookeeper-1/bin/zkServer.sh status
/usr/local/zk/zookeeper-2/bin/zkServer.sh status
/usr/local/zk/zookeeper-3/bin/zkServer.sh status
模拟挂掉服务器
/usr/local/zk/zookeeper-3/bin/zkServer.sh stop
结论:3节点集群,2各挂掉,主服务器也无法运行,因为可运行的及其没有超过集群总数量的半数。
当各个节点的通信不良时候,集群中的节点监听不到leader节点的心跳, 就会认为leader节点出了问题, 此时集群将分裂为不同的小集群, 这些小集群会各自选举出自己的leader节点, 导致原有的集群中出现多个leader节点.
ZooKeeper默认采用了Quorums(法定人数)的方式: **只有获得超过半数节点的投票, 才能选举出leader.**就是确保要么选出唯一的leader, 要么选举失败.
ZooKeeper维护了一个叫epoch的变量, 每当新leader产生时, epoch都会递增, followers如果确认了新的leader存在, 同时也会知道其epoch的值 —— 它们会拒绝epoch小于现任leader的epoch的所有旧leader的任何请求.
zk作为非常重要的分布式协调组件,集群会以一主多从的形式进行部署。zk是强数据一致性的(CP),所以用到了ZAB(原子广播协议)协议,zk节点崩溃的时候,主从同步问题。
当leader宕机的时候,follower节点发现,会重新选举新的leader。此时集群不对外提供服务。
也就是zk为什么是CP的,必须半数以上的节点已经写入的文件中,才向内存提交。
介绍下Zookeeper是什么?
Zookeeper有什么作用?优缺点?有什么应用场景?
Zookeeper的选举策略,leader和follower的区别?
Zookeeper的节点类型有哪些?分别作用是什么?
Zookeeper的节点数怎么设置比较好?
Zookeeper架构
Zookeeper的数据结构(树)?基于它实现的分布式锁?基于它实现的Master选举?基于它的集群管理? Zookeeper的注册(watch)机制使用场景?
介绍下Zookeeper消息的发布订阅功能
Zookeeper的分布式锁实现方式?
Zookeeper怎么保证一致性的
Zookeeper的zab协议(原子广播协议)?
ZAB是以什么算法为基础的?ZAB流程?
Zookeeper的通知机制
Zookeeper脑裂问题
Zookeeper的Paxos算法
Zookeeper的协议有哪些?
Zookeeper的数据存储在什么地方?
Zookeeper从三台扩容到七台怎么做?