Zookeper相关知识
一.Zookeeper原理架构
1. 什么是Zookeeper
学一个东西,不搞明白他是什么东西,哪还有心情学啊!!
首先,Zookeeper是Apache的一个java项目,属于Hadoop系统,扮演管理员的角色。
然后看到官网那些专有名词,实在理解不了。
在Zookeeper的官网上有这么一句话:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
那么我们来仔细研究一下这个东西吧!
2. Zookeeper有哪些用途
2.1. 配置管理
这个好理解。分布式系统都有好多机器,比如我在搭建hadoop的HDFS的时候,需要在一个主机器上(Master节点)配置好HDFS需要的各种配置文件,然后通过scp命令把这些配置文件拷贝到其他节点上,这样各个机器拿到的配置信息是一致的,才能成功运行起来HDFS服务。Zookeeper提供了这样的一种服务:一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。这样就省去手动拷贝配置了,还保证了可靠和一致性。
2.2. 名字服务
这个可以简单理解为一个电话薄,电话号码不好记,但是人名好记,要打谁的电话,直接查人名就好了。
分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务;
类似于域名与ip之间对应关系,域名容易记住;
通过名称来获取资源或服务的地址,提供者等信息
2.3. 分布式锁
碰到分布二字貌似就难理解了,其实很简单。单机程序的各个进程需要对互斥资源进行访问时需要加锁,那分布式程序分布在各个主机上的进程对互斥资源进行访问时也需要加锁。很多分布式系统有多个可服务的窗口,但是在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。举个通俗点的例子,比如银行取钱,有多个窗口,但是呢对你来说,只能有一个窗口对你服务,如果正在对你服务的窗口的柜员突然有急事走了,那咋办?找大堂经理(zookeeper)!大堂经理指定另外的一个窗口继续为你服务!
2.4. 集群管理
在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中有些机器(比如Master节点)需要感知到这种变化,然后根据这种变化做出对应的决策。我已经知道HDFS中namenode是通过datanode的心跳机制来实现上述感知的,那么我们可以先假设Zookeeper其实也是实现了类似心跳机制的功能吧!
3. Zookeeper的特点
1 最终一致性:为客户端展示同一视图,这是zookeeper最重要的功能。
2 可靠性:如果消息被到一台服务器接受,那么它将被所有的服务器接受。
3 实时性:Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
4 等待无关(wait-free):慢的或者失效的client不干预快速的client的请求。
5 原子性:更新只能成功或者失败,没有中间状态。
6 顺序性:所有Server,同一消息发布顺序一致。
用到Zookeeper的系统
HDFS中的HA方案
YARN的HA方案
HBase:必须依赖Zookeeper,保存了Regionserver的心跳信息,和其他的一些关键信息。
Flume:负载均衡,单点故障
4. Zookpeeper的基本架构
1 每个Server在内存中存储了一份数据;
2 Zookeeper启动时,将从实例中选举一个leader(Paxos协议);
3 Leader负责处理数据更新等操作(Zab协议);
4 一个更新操作成功,当且仅当大多数Server在内存中成功修改
数据。
4.1 Zookpeeper Server 节点的数目
Zookeeper Server数目一般为奇数
Leader选举算法采用了Paxos协议;Paxos核心思想:当多数Server写成功,则任务数据写
成功。也就是说:
如果有3个Server,则两个写成功即可;
如果有4或5个Server,则三个写成功即可。
Server数目一般为奇数(3、5、7)
如果有3个Server,则最多允许1个Server挂掉;
如果有4个Server,则同样最多允许1个Server挂掉
既然如此,为啥要用4个Server?
4.2 Observer节点
3.3.0 以后 版本新增角色Observer
增加原因:
Zookeeper需保证高可用和强一致性;
当集群节点数目逐渐增大为了支持更多的客户端,需要增加更多Server,然而Server增多,投票阶段延迟增大,影响性能。为了权衡伸缩性和高吞吐率,引入Observer:
Observer不参与投票;
Observers接受客户端的连接,并将写请求转发给leader节点;
加入更多Observer节点,提高伸缩性,同时不影响吞吐率。
5. Zookeeper写流程:
客户端首先和一个Server或者Observe(可以认为是一个Server的代理)通信,发起写请求,然后Server将写请求转发给Leader,Leader再将写请求转发给其他Server,Server在接收到写请求后写入数据并相应Leader,Leader在接收到大多数写成功回应后,认为数据写成功,相应Client。
6. Zookeeper数据模型
组织结构
zookeeper采用层次化的目录结构,命名符合常规文件系统规范;
每个目录在zookeeper中叫做znode,并且其有一个唯一的路径标识;
Znode
Znode可以包含数据和子znode(ephemeral类型的节点不能有子znode);
Znode中的数据可以有多个版本,比如某一个znode下存有多个数据版本,那么查询这个路径下的数据需带上版本;
客户端应用可以在znode上设置监视器(Watcher)
znode不支持部分读写,而是一次性完整读写
Znode类型
Znode有两种类型,短暂的(ephemeral)和持久的(persistent);
Znode的类型在创建时确定并且之后不能再修改;
ephemeral znode的客户端会话结束时,zookeeper会将该ephemeral znode删除,ephemeralzn ode不可以有子节点;
persistent znode不依赖于客户端会话,只有当客户端明确要删除该persistent znode时才会被删除;
Znode有四种形式的目录节点,PERSISTENT、PERSISTENT_SEQUENTIAL、EPHEMERAL、PHEMERAL_SEQUENTIAL。
二. Zookeeper集群搭建
1. Zookeeper集群搭建
第一步:需要安装jdk环境。
第二步:把zookeeper的压缩包上传到服务器。
第三步:解压缩。
第四步:把zookeeper复制三份。
[root@localhost ~]# mkdir /usr/local/solr-cloud [root@localhost ~]# cp -r zookeeper-3.4.6 /usr/local/solr-cloud/zookeeper01 [root@localhost ~]# cp -r zookeeper-3.4.6 /usr/local/solr-cloud/zookeeper02 [root@localhost ~]# cp -r zookeeper-3.4.6 /usr/local/solr-cloud/zookeeper03
1.1. 创建Zookeeper serverID标识myid
第五步:在每个zookeeper目录下创建一个data目录。
第六步:在data目录下创建一个myid文件,文件名就叫做“myid”。内容就是每个实例的id。例如1、2、3
[root@localhost data]# echo 1 >> myid [root@localhost data]# ll total 4 -rw-r--r--. 1 root root 2 Apr 7 18:23 myid [root@localhost data]# cat myid 1
1.2. 修改zoo.cfg,Zookeeper互联
第七步:修改配置文件。把conf目录下的zoo_sample.cfg文件改名为zoo.cfg
server.X=A:B:C
X-代表服务器编号
A-代表ip
B和C-代表端口,这个端口用来系统之间通信
2881是Zookeeper集群的通信端口,3881是Zookeeper集群的选举端口
server.1=192.168.25.154:2881:3881 server.2=192.168.25.154:2882:3882 server.3=192.168.25.154:2883:3883
1.3. 启动Zookeeper实例
第八步:启动每个zookeeper实例。
启动:bin/zkServer.sh start
查看zookeeper的状态:
查看:bin/zkServer.sh status
三 .Zookeeper常用
3.1 zkCli客户端
默认链接localhost
bin/zkCli.sh
连接指定主机
bin/zkCli.sh -server hpd-01 -p 2181
3.1.1 管理数据
ls-查看节点
ls /
ls /zookeeper
3.1.2、get-查看数据
get /zookeeper
3.1.3、create-创建节点
命令行只能存放字符串数据,没有办法存放二进制数据
"hellozk"之后的数据全部是该节点的元数据信息,目的是为了维护数据版本的一致性。
create /aa "hellozk"
3.1.4、set-修改节点的值
数据版本会增加1
set /aa hellospark
3.1.5、rmr-递归删除数据节点
rmr /aa
4.2、监听节点
注意:注册的监听器在正常收到一次所监听的事件后,就失效。
4.2.1、get注册监听
get注册监听,只有当监听节点的数据发生变化,才会出发,监听节点添加新节点不会出发注册的监听器;
且一次监听注册只起一次作用,触发之后,失效。除非再次注册。
监听/aa 节点的数值变化,一旦/aa节点的值发生了变化(假如值被hdp-03上的zkCli修改了),向zk注册watch事件的客户端(假如是hdp-01上的zkCli)就会收到zk的通知,则hdp-01上的zkCli就会收到zk发回的通知,当然zkCli收到通知后只是输出到了控制台。
get /aa watch
会看到,状态(表示和服务器链接状态良好),以及事件的类型type(数据节点的值发生了变化),以及哪一个节点
4.2.2、ls-注册子节点变化事件
ls是获取子节点,监听子节点事件用 ls /aa watch
hdp-01的zkCli注册ls 监听
ls /aa watch
hdp-02的zkCli对aa节点添加字节点,hdp-01会收到zk的事件通知。
create /aa/xx 132
6、java客户端
6.1、创建zk客户端
timeOut指定会话超时时间,即客户端关闭连接后,会话还可以保持多久;
watcher是一个接口,即为当客户端收到zk事件通知时候要进行什么逻辑操作。
//构造一个了解Zookeeper的客户端对象
ZooKeeper zk = new ZooKeeper(“hdp-01:2181,hdp-02:2181,hdp-03:2181”,timeOut,watcher)
6.2、增删改查
6.2.1、创建节点create
byte[] 数据 要求不能大于1M
ACL是访问权限Access Control List:什么样的人可以访问这个数据节点,一般是内部一套系统使用,这里就选择开放权限
String path = zk.create(String path,byte[] data,List<ACL> acl,CreateMode createMode);
节点类型
6.2.2、修改数据setData
version指明要修改哪个版本的数据,如果用户不关心,哪个版本,可以设置成-1,表示修改全部版本。
返回该节点数据的元数据Stat
6.2.3、查找数据getData
watch:表示要不要监听
Stat:用元数据来表示要回去哪个版本,最新版本用null表示
6.2.4、查找子节点getChildren
返回所有子节点的名字,不带全路径,只有子节点名字
6.2.5、删除节点delete
-1表示删除所有版本
6.3、注册监听
注意:注册的监听器在正常收到一次所监听的事件后,就失效。
zk会另外起一个线程,去等待zk发来的通知,并作出我们设置的watcher逻辑(若没有设置watcher逻辑,而是是指了boolean true,则直接输出收到的通知)
Watcher是一个接口:客户端收到监听事件后的回调逻辑
6.3.1、get监听节点数据变化
@Before public void init() throws Exception { // 构造一个连接zookeeper的客户端对象 zk = new ZooKeeper("hdp-01:2181,hdp-02:2181,hdp-03:2181", 2000, null); } @Test public void testGetWatch() throws Exception { byte[] data = zk.getData("/mygirls", true, null); // 监听节点数据变化 List<String> children = zk.getChildren("/mygirls", true); //监听节点的子节点变化事件 System.out.println(new String(data, "UTF-8")); Thread.sleep(Long.MAX_VALUE); }
注意:注册的监听器在正常收到一次所监听的事件后,就失效。可以在new Zookeeper(创建客户端)的时候,指定默认的回调逻辑同事在回调逻辑中继续注册监听,以后直接在getData时写true就可以了,这样就可以一直监听。
注意:new Zookeeper的时候,会出发连接成功事件(发生路径null,type类型none)
可以用转太判断来避开初始事件
构造Zookeeper的时候,添加回调逻辑,后续getData是监听节点数据是注册监听就只要设置true,那么就会调用对象构造时候的回调逻辑(默认逻辑)。
6.3.2、监听节点的子节点变化事件
6.4、Zookeeper客户端工作线程
sendThread
eventThread
Zookeeper中的eventThread是守护线程
守护线程: A是B的守护线程,若B线程结束,不管A线程有没有执行完毕,A线程都会退出。(主人已挂,仆人陪葬)
thread.setDaemon(true);