一: zookeeper概述
Zookeeper基本介绍
官方网址:http://zookeeper.apache.org/
Zookeeper是一个分布式的协调服务框架,Zookeeper可以解决分布式环境常见的问题:统一命名服务,信息配置管理,数据一致性,集群管理,分布式锁等等。
1)分布式环境下带来的难题
1.容易出现死锁
2.容易活锁,处于活锁的线程都是非阻塞的,而且每个线程都抢不到资源,会造成cpu的耗费。线程在执行过程中产生了碰撞——再执行——再碰撞,如此循环往复,形成活锁。
3.集群的管理问题,比如某台的宕机需要能够检测到
4.集群配置文件的统一管理问题
5.集群中信息状态的更新通知问题
6.管理集群的选举问题
7.分布式锁的实现,这需要用新的机制和技术来实现
Zookeeper非常重要:后期Hadoop集群会通过Zookeeper,Hbase集群、Storm集群、Kafka集群都会用到Zookeeper。
二: zookeeper单机模式安装配置
安装步骤:
0. 关闭虚拟机的防火墙 ,执行:service iptables stop
1. 准备虚拟机,安装并配置jdk 1.6以上
Jdk环境变量配置示例:
JAVA_HOME=/home/software/jdk1.8
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
PATH=$JAVA_HOME/bin:$PATH
exportJAVA_HOME PATH CLASSPATH
2. 上传zookeeper的安装包
3.解压安装 tar -xvf …………
4.进入zookeeper安装目录下的conf目录,有一个zoosample.cfg的文件
复制一份,并重命名为zoo.cfg文件,这个名字固定写死,因为zookeeper启动会检查这个文件,根据这个配置文件里的信息来启动服务
5.进入bin目录
执行:./zkServer.sh start 启动zookeeper
6.进入zookeeper客户端,操作zookeeper
执行:./zkCli.sh
三: zk基础概念及指令
知识点:
1.Zk有一个 根节点 /。对于zk的操作,都是基于根节点来操作的
2.每个节点都可以创建自己的子节点
3.每个节点都叫做 znode节点
4.每个znode节点都可以存储数据
5.多个znode节点共同形成一棵znode树
6.znode树会存在于zk服务端的内存中,供用户快速查询。此外,为了防止数据丢失,znode树数据也会落地到磁盘上,会存到zoo.cfg 里dataDir指定的目录下。
7.znode的路径是全局唯一的。我们可以基于此特性,做集群的naming(命名服务),确保命名的唯一性。
8.创建节点时,需要指定初始数据,否则不能创建节点。
9.Zookeeper是事务的概念,针对每个事务(写操作,比如创建节点,更新节点,删除节点),zk都会分配一个递增的Zxid(事务id)。其中:cZxid=》创建节点的事务id mZxid=》修改的事务id pZxid=》指定节点下最近的一次事务id。
10.zookeeper的节点类型一种4种:
①create /park01 普通持久节点
②create -e /park01 普通临时节点
③create -s /park01 顺序持久 节点
④create -e -s /park01 临时顺序节点
以上四种节点,重点记住 临时节点。因为zk主要根据临时节点的状态,去监听对应客户端的状态。因为临时节点消失,意味着创建此节点的客户端掉线。后期很多大数据框架的检测,都是基于此机制来实现的。
Zookeeper服务端指令
指令 | 说明 |
sh zkServer.sh start | 启动zk服务的 |
sh zkServer.sh stop | 停止zk服务 |
sh zkServer.sh restart | 重启zk服务 |
sh zkServer.sh status | 查看zk服务角色,有: Observer |
sh zkCli.sh | 进入zk客户端 |
Zookeeper客户端指令
指令 | 说明 | 示例 |
ls | 查看 | ls / 查看根路径 |
create | 创建 | create /park02 "" |
get | 获取指定节点信息 | cZxid = 0x2 #创建此节点的事务id ctime = Wed Jan 17 10:55:25 PST 2018 #创建此节点的时间戳 mZxid = 0x2 #修改此节点的事务id mtime = Wed Jan 17 10:55:25 PST 2018 #修改此节点的时间戳 pZxid = 0x4 cversion = 1 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 #如果此节点不是临时节点,则为0 dataLength = 9 #数据长度 numChildren = 1 #子节点数量 |
set | 更新节点数据 | set /park01 hellozk |
delete | 删除子节点为空的节点 | delete /park01 |
rmr | 递归删除指定节点 | rmr /park02 |
quit (或ctrl+c) | 退出客户端 |
|
create -e | 创建临时节点,当创建此节点的客户端下线时,节点被删除。 |
|
create -s | 创建顺序节点,每次创建节点时,会跟上一个递增的顺序号 |
|
create -e -s | 创建临时顺序节点 |
|
三: zookeeperAPI操作
2016年8月20日
21:19
同步方式
一、
连接zookeeper和创建节点数据代码:
public class ZkDemo {
public static voidmain(String[] args) throws Exception {
final CountDownLatch cdl=new CountDownLatch(1);
//第一个参数,连接的zookeeper节点ip,
//第二个参数,会话超时时间,以毫秒为单位。比如设置1000*30 是30秒。如果30秒内zookeeper没有收到客户端节点的心跳,则断开连接
//第三个参数,Watcher,观察者,当zookeeper里有事件发生时,会通知到这个process方法里
//需要注意,连接是异步的,当连接成功是,会回调process方法,我们需要做相应的处理
//此外,连接一个zookeeper连接地址也可以,因为zookeep会将数据同步至其他的zookeerper节点
ZooKeeper zk=newZooKeeper("192.168.234.137:2181",30000,new Watcher(){
@Override
public voidprocess(WatchedEvent event) {
if(event.getState()==KeeperState.SyncConnected){
//说明连接成功了
cdl.countDown();
}
}
});
//之所以用递减锁,因为:连接是非阻塞的,到底连接成功没,不知道。所以,用递减锁,把这个连接变成了阻塞式连接,当成功连接后,才走
//await()之后的代码
cdl.await();
//创建节点
//1.path 节点路径;2.data[]节点数据;3.acl 权限策略,一般设为所用用户都可以操作这个节点,并且具有所有权限,所以选:Ids.OPEN_ACL_UNSAFE;
//4.createMode创建模式:
//PERSISTENT持久
//PERSISTENT_SEQUENTITAL持久顺序
//EPHEMERAL 临时
//EPHEMERAL_SEQUENTITAL临时顺序
zk.create("/node","hello1604Zookeeper".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
//也可以通过StringCallBack()回调函数来拿到创建后的节点名。rc 是返回码。0代表节点创建成功。-110代表此节点已存在,节点创建失败。
//ctx 可以是任意一个对象,这个对象也可以回调函数里接收
//需要注意的是,如果是用这种方式来创建节点,是非阻塞的
zk.create("/02","helloZK".getBytes(),Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT,new StringCallback() {
public voidprocessResult(int rc, String path, Object ctx, String name) {
System.out.println(rc+":"+path+":"+ctx+":"+name);
}
}, 1);
}
}
二、
更新节点数据:
@Test
public void testSet()throws Exception{
final CountDownLatchcdl=new CountDownLatch(1);
ZooKeeper zk=newZooKeeper("192.168.234.137:2181",30000,new Watcher(){
@Override
public voidprocess(WatchedEvent event) {
if(event.getState()==KeeperState.SyncConnected){
cdl.countDown();
}
}
});
cdl.await();
//version版本号的作用:在指定路径下,有一个dataVersion,这个是数据更改的版本号,从0开始,在此节点下
//每更改一次数据,版本号递增1
//而setData(,,version)的version的意思是:指定一个版本号,基于这个版本号来修改,但是需要注意:
//如果指定的版本号是3,而当前的dataVersion是4,即指定的版本号不存在(已过时),则会报错
//如果指定的版本号是-1,则说明无论当前dataVersion是多少,都会进行数据的更新,最常用的就是-1
zk.setData("/node","ccc".getBytes(), -1);
}
三
获取节点数据
@Test
public void testGetData()throws Exception{
final CountDownLatchcdl=new CountDownLatch(1);
ZooKeeper zk=newZooKeeper("192.168.234.137:2181",3000,new Watcher(){
@Override
public voidprocess(WatchedEvent event) {
cdl.countDown();
}
});
cdl.await();
//watch,当指定节点发生监听的事件时,会触发Watcher里的方法,
//监听的事件有:1.节点数据发生变化2.节点创建 3.节点删除 4.节点的子节点发生变化。但是监听的事件只会触发一次回调方法。
//stat,节点环境信息,一般设置为null即可。如果想要,可以传入一个空的Stat对象来接收
byte[]data=zk.getData("/node",new Watcher(){
@Override
public voidprocess(WatchedEvent event) {
if(event.getType()==EventType.NodeDataChanged){
System.out.println("节点数据发生变化");
}
}
},null);
System.out.println(newString(data));
//保持线程开启
//getACL,得到指定路径的权限信息,权限信息被封装在返回的集合里
//此外,也可以传入一个Stat对象,用来分装节点的环境信息,比如zxid,ctime等等
Stat s1=new Stat();
List<ACL>list=zk.getACL("/02",s1);
for(ACL a:list){
System.out.println(a);
}
System.out.println(s1);
while(true){}
}
四
获取子节点
@Test
public voidtestGetChildren() throws Exception{
//连接代码省略
List<String> list=zk.getChildren("/", null);
for(Strings:list)System.out.println(s);
}
五
删除节点
@Test
public void testDelete()throws Exception{
//连接代码略
zk.delete("/node",-1);
}
六
判断节点是否存在
@Test
public void testExists()throws Exception{
//如果节点不存在,stat返回值=null
//如果节点存在,stat返回节点的元数据
Statstat=zk.exists("/node02", null);
System.out.println(stat);
}
异步方式
七
异步创建节点
@Test
public voidtestCreateByAsynchronous() throws Exception{
zk.create("/node","helloworld".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,
newStringCallback() {
public voidprocessResult(int rc, String path, Object ctx, String name) {
System.out.println("节点创建成功"+rc+":"+path+":"+ctx+":"+name);
}
},null);
}
利用监听,判断节点是否被删除或创建事件
代码:
zk.exists("/park02",new Watcher(){
@Override
public voidprocess(WatchedEvent event) {
if(event.getType()==EventType.NodeDeleted){
System.out.println("节点被删除");
}
if(event.getType()==EventType.NodeCreated){
System.out.println("节点被创建");
}
}
});
利用监听,判断子节点状态变化事件
代码:
zk.getChildren("/park01",newWatcher(){
@Override
public voidprocess(WatchedEvent event) {
if(event.getType()==EventType.NodeChildrenChanged){
System.out.println("子节点发生变化");
}
}
});
EventType 状态的类
四: zookeeper集群安装配置
安装步骤:
提示:要关闭虚拟机的防火墙,执行:service iptables stop
1.准备虚拟机,安装并配置jdk,1.6以上
2.上传zookeeper的安装包 3.4.7版本
3.解压安装 tar -xvf …………
4.配置zookeeper。
5.配置伪集群模式
①切换到zookeeper安装目录的conf目录,其中有一个zoo_sample.cfg的配置文件,这个一个配置模板文件,我们需要复制这个文件,并重命名为 zoo.cfg。zoo.cfg才是真正的配置文件
②配置zoo.cfg=》vim zoo.cfg 更改如下几个参数配置:
dataDir。这个参数是存放zookeeper集群环境配置信息的。这个参数默然是配置在 /tmp/zookeeper下的。但是注意,tmp是一个临时文件夹,这个是linux自带的一个目录,是linux本身用于存放临时文件用的目录。但是这个目录极有可能被清空,所以,重要的文件一定不要存在这个目录下。
所以改成:/home/work/zkdata
注意:这个路径是自定义的,所以目录需要手动创建
clientport。客户端连接服务器的端口,默认是2181,一般不用修改
在配置文件里,需要在加上如下的配置:
server.1=192.168.234.10:2888:3888
server.2=192.168.234.11:2888:3888
server.3=192.168.234.12:2888:3888
①server是关键字,写死
②后面的数字是选举id,在zk集群的选举过程中会用到。
补充:此数字不固定,但是需要注意选举id不能重复,相互之间要能比较大小
然后保存退出
③192.168.234.10:2888:3888
说明:2888原子广播端口,3888选举端口
zookeeper有几个节点,就配置几个server,
③配置文件配置好,需要在dataDir目录下创建一个文件
即在:/home/work/zkdata 目录下,创建 myid
vim myid
给当前的节点编号。zookeeper节点在启动时,就会到这个目录下去找myid文件,得知自己的编号
保存退出
6.配置伪集群环境的其他节点
scp-r 目录 远程ip地址:存放的路径
scp -r/home/software/zookeeper 192.168.234.151: /home/
①更改节点的ip
②更改myid的id号
③关闭防火墙 ,执行:service iptables stop;
7.启动zookeeper
进入到zookeeper安装目录的bin目录
执行:./zkServer.sh start
然后可以输入jps命令,查看有哪些java进程,
执行:jps
8.执行 :./zkServer.sh status 查看当前zookeeper节点状态
结果发现报错,原因是现在是集群,但是之后一台节点启动,不能正常工作。
9.其他两台节点启动zookeeper服务
报错及解决办法:
配置说明:
tickTime: zookeeper中使用的基本时间单位, 毫秒值.
dataDir: 数据目录. 可以是任意目录.
dataLogDir: log目录, 同样可以是任意目录. 如果没有设置该参数, 将使用和dataDir相同的设置.
clientPort: 监听client连接的端口号
initLimit: zookeeper集群中的包含多台server, 其中一台为leader, 集群中其余的server为follower. initLimit参数配置初始化连接时, follower和leader之间的最长心跳时间. 此时该参数设置为5, 说明时间限制为5倍tickTime,即5*2000=10000ms=10s.
syncLimit: 该参数配置leader和follower之间发送消息, 请求和应答的最大时间长度. 此时该参数设置为2, 说明时间限制为2倍tickTime, 即4000ms.
Zookeeper选举机制
2018年6月6日
17:11
概述
Zookeeper的选举机制:
分两个阶段。
阶段一:数据恢复阶段。每台zk启动时,会从dataDir目录找到最大事务id (Zxid)
阶段二:选举阶段。每台zk服务都会推荐自己当Leader,并且会提交选举协议,协议的内容:
①自己拥有的最大事务id
②自己的选举id
③逻辑时钟值。作用是确保每台zk在同一轮选举中
④当前的状态:
Looking(选举)
Follower
Leader
Observer(观察者)
pk原则:
①先比较最大事务id,谁大谁当Leader。因为事务id越大,事务越新。
②如果最大事务id比较不出来,就比较选举id,谁大谁当Leader。
但是注意:pk最基本的原则要满足过半性
补充:过半性是zookeeper是一大特点。有:①过半选举 ②过半存活
所以,一般搭建zk集群,都是奇数台服务器,比如11、25等。因为可以更好的满足过半性。