ZooKeeper基础知识及环境搭建

基础入门


Zookeeper概述

ZooKeeper是一种分布式协调服务,所谓分布式协调服务可以在分布式系统中共享配置,协调锁资源,提供命名服务。
zookeeper是基于内存同步数据的,所以集群内的节点其内存中的数据结构是完全相同的,因此效率非常高。

下载地址:https://zookeeper.apache.org/releases.html#download

Zookeeper特性

  • 顺序一致性:从同一个客户端发起的事务请求,最终将会严格按照其发起顺序被应用到zookeeper中
  • 可靠性:消息message被到一台服务器接受,那么它到任何服务器都被接受。
  • 实时性:zk保证在一个时间间隔范围内获得服务器的更新信息,或者服务器失效信息。但是由于网络延时等一些其他原因,zk不能保证两个客户端同事得到跟新或者失效信息。
  • 原子性:更新只能成功或者失败,没有其他中间信息。
  • 单一视图:无论客户端连接的是哪个zookeeper服务器,其看到的服务端数据模型都是一致的

Zookeeper数据模型

Zookeeper的数据模型很像数据结构当中的树,也很像文件系统的目录。树是由节点所组成,Zookeeper的数据存储也同样是基于节点,这种节点叫做Znode。但是,不同于树的节点,Znode的引用方式是路径引用,类似于文件路径,这样的层级结构,让每一个Znode节点拥有唯一的路径,就像命名空间一样对不同信息作出清晰的隔离。

Znode包含了数据、子节点引用、访问权限等等:
这里写图片描述
data:Znode存储的数据信息。
ACL:记录Znode的访问权限,即哪些人或哪些IP可以访问本节点。
stat:包含Znode的各种元数据,比如事务ID、版本号、时间戳、大小等等。
child:当前节点的子节点引用,类似于二叉树的左孩子右孩子

注意:Zookeeper是为读多写少的场景所设计。Znode并不是用来存储大规模业务数据,而是用于存储少量的状态和配置信息,每个节点的数据最大不能超过1MB。

节点类型:

ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。

  • 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。
  • 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
  • 顺序节点:当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。
  • 监视器:客户端可以在节点上设置watch,我们称之为监视器。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次,这样可以减少网络流量。

Zookeeper的基本操作和事件通知

那么我们如何来操作这些节点呢?
zookeeper为我们提供了一些简单的API,甚至还提供了触发器机制。

常用API:

create:创建节点
delete:删除节点
exists:判断节点是否存在
getData:获得一个节点的数据
setData:设置一个节点的数据
getChildren:获取节点下的所有子节点

这其中,existsgetDatagetChildren属于读操作。Zookeeper客户端在请求读操作的时候,可以选择是否设置Watch

Watch:可以理解成是注册在特定Znode上的触发器。当这个Znode发生改变,也就是调用了create,delete,setData方法的时候,将会触发Znode上注册的对应事件,请求Watch的客户端会接收到异步通知。

Watcher数据变更通知:
Zookeeper使用Watcher机制实现分布式数据的发布/订阅功能。
Zookeeper的Watcher机制主要包括客户端线程、客户端WatcherManager、Zookeeper服务器三部分。客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中。当Zookeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑。

Zookeeper的一致性

身为分布式系统的协调服务,为了防止单机挂掉的情况,zookeeper维护了一个集群:
这里写图片描述
Zookeeper Service集群是一主多从结构。在更新数据时,首先更新到主节点(这里的节点是指服务器,不是Znode),再同步到从节点。在读取数据时,直接读取任意从节点。

集群角色:

角色说明
Leader集群内部各服务器的调度者,事务请求的唯一调度和处理者,保证集群事务处理的顺序性。负责投票的发起和决议,更新系统状态,处理写请求。
Follower参与客户端非事务请求,转发事务请求给Leader服务器,参与请求的Proposal投票,参与Leader选举投票。
ObserverObServer可以接受客户端连接,将写请求转发给leader节点,但ObServer不参加投票过程,只同步leader状态。ObServer的目的是为了扩展系统,提高读取速度。
Client执行读写请求的发起方

ZAB协议:

为了保证主从节点的数据一致性,Zookeeper采用了ZAB协议,这种协议非常类似于一致性算法Paxos和Raft。
ZABZookeeper Atomic Broadcast,有效解决了zookeeper集群崩溃恢复,以及主从同步数据的问题。

在学习ZAB之前,我们需要首先了解ZAB协议所定义的三种节点状态:

Looking :选举状态。
Following :Follower节点(从节点)所处的状态。
Leading :Leader节点(主节点)所处状态。

我们还需要知道最大ZXID的概念:最大ZXID也就是节点本地的最新事务编号,包含epoch和计数两部分。epoch是纪元的意思,相当于Raft算法选主时候的term。

崩溃恢复:

假如Zookeeper当前的主节点挂掉了,集群会进行崩溃恢复。ZAB的崩溃恢复分成三个阶段:
1.Leader election:选举阶段
此时集群中的节点处于Looking状态。它们会各自向其他节点发起投票,投票当中包含自己的服务器ID和最新事务ID(ZXID);
接下来,节点会用自身的ZXID和从其他节点接收到的ZXID做比较,如果发现别人家的ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的ZXID所属节点;
每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其他节点的状态变为Following。
2.Discovery:发现阶段
用于在从节点中发现最新的ZXID和事务日志。或许有人会问:既然Leader被选为主节点,已经是集群里数据最新的了,为什么还要从节点中寻找最新事务呢?
这是为了防止某些意外情况,比如因网络原因在上一阶段产生多个Leader的情况。
所以这一阶段,Leader集思广益,接收所有Follower发来各自的最新epoch值。Leader从中选出最大的epoch,基于此值加1,生成新的epoch分发给各个Follower。
各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志。Leader选出最大的ZXID,并更新自身历史日志。
3.Synchronization:同步阶段
把Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。

Broadcast:

ZAB是怎么实现写入数据的呢?
写入数据涉及到ZAB协议的Broadcast阶段。

什么是Broadcast呢?简单来说,就是Zookeeper常规情况下更新数据的时候,由Leader广播到所有的Follower。其过程如下:

  1. 客户端发出写入数据请求给任意Follower。
  2. Follower把写入数据请求转发给Leader。
  3. Leader采用二阶段提交方式,先发送Propose广播给Follower。
  4. Follower接到Propose消息,写入日志成功后,返回ACK消息给Leader。
  5. Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower。

这里写图片描述

Zab协议既不是强一致性,也不是弱一致性,而是处于两者之间的单调一致性。它依靠事务ID和版本号,保证了数据的更新和读取是有序的

Zookeeper应用场景

1.分布式锁:这是雅虎研究员设计Zookeeper的初衷。利用Zookeeper的临时顺序节点,可以轻松实现分布式锁。
2.服务注册和发现:利用Znode和Watcher,可以实现分布式服务的注册和发现。最著名的应用就是阿里的分布式RPC框架Dubbo。
3.共享配置和状态信息:Redis的分布式解决方案Codis,就利用了Zookeeper来存放数据路由表和 codis-proxy 节点的元信息。同时 codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy。
此外,Kafka、HBase、Hadoop,也都依靠Zookeeper同步节点信息,实现高可用。
。。。

环境部署


ZooKeeper的工作模式有三种:单机模式、集群模式、伪集群模式。

  • 单机模式:Zookeeper只运行在一台服务器上,适合测试环境;
  • 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例;
  • 集群模式:Zookeeper运行于一个至少有三个节点以上集群中,适合生产环境;

伪集群模式

在同一台机器上部署3个zookeeper server时,需要为每个server创建独立的配置文件,并且保证每个配置文件中的端口不能冲突,除了clientPort不同之外,另外,还要在dataDir所对应的目录中创建myid文件来指定对应的zookeeper server 实例。

注意事项:
clientPort端口:如果在1台机器上部署多个server,那么每台机器都要不同的 clientPort,比如 server1是2181,server2是2182,server3是2183
dataDir和dataLogDir:dataDir和dataLogDir也需要区分下,将数据文件和日志文件分开存放,同时每个server的这两变量所对应的路径都是不同的
server.X和myid: server.X 这个数字就是对应,data/myid中的数字。在3个server的myid文件中分别写入了0,1,2,那么每个server中的zoo.cfg都配 server.0 server.2,server.3就行了。因为在同一台机器上,后面连着的2个端口,3个server都不要一样,否则端口冲突

启动命令:

bin/zkServer.sh start zoo1.cfg
bin/zkServer.sh start zoo2.cfg
bin/zkServer.sh start zoo3.cfg

集群模式

实验环境:(搭建elk平台的kafka集群,本节先部署zookeeper)

IPServermyid
192.168.20.201Kafka+ZooKeeper1
192.168.20.203Kafka+ZooKeeper2
192.168.20.56Kafka+ZooKeeper3

zk服务器集群规模不得小于3个节点
要求各服务器之间系统时间要保持一致

1.在三个节点上解压程序,创建数据库目录,安装JDK:

#解压程序:
tar xf zookeeper-3.4.12.tar.gz -C /usr/local/

#创建数据库目录:
mkdir /data/zookeeper/

#安装JDK
rpm -ivh jdk-8u45-linux-x64.rpm
echo -e 'export JAVA_HOME=/usr/java/jdk1.8.0_45\nexport CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH\nexport PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$HOMR/bin'>>/etc/profile
source  /etc/profile

2.在192.168.20.201节点上修改配置文件,然后同步到其他两个节点上:

cd /usr/local/zookeeper-3.4.12/conf/	
cp zoo_sample.cfg zoo.cfg

#编辑修改配置文件:
# grep -Ev "^$|#" zoo.cfg 
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/
clientPort=2181
server.1=192.168.20.201:2888:38888
server.2=192.168.20.203:2888:38888
server.3=192.168.20.56:2888:38888

#同步配置文件到其他两台节点:
scp zoo.cfg root@192.168.20.203:/usr/local/zookeeper-3.4.12/conf/
scp zoo.cfg root@192.168.20.56:/usr/local/zookeeper-3.4.12/conf/

注: zookeeper 集群,每个节点的配置文件都是一样的。所以直接同步过去,不需要做任何修改。

3.创建myid文件

#192.168.20.201
echo 1 > /data/zookeeper/myid	

#192.168.20.203
echo 2 > /data/zookeeper/myid

#192.168.20.56
echo 3 > /data/zookeeper/myid

4.启动服务&查看节点状态

#192.168.20.201
/usr/local/zookeeper-3.4.12/bin/zkServer.sh start
/usr/local/zookeeper-3.4.12/bin/zkServer.sh status

ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: follower

#192.168.20.203
/usr/local/zookeeper-3.4.12/bin/zkServer.sh start
/usr/local/zookeeper-3.4.12/bin/zkServer.sh status

ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: leader

#192.168.20.56
/usr/local/zookeeper-3.4.12/bin/zkServer.sh start
/usr/local/zookeeper-3.4.12/bin/zkServer.sh status

ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: follower

配置文件详解

tickTime=2000      # 这个时间是作为Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,每隔tickTime时间就会发送一个心跳;最小的session过期时间为2倍tickTime 
initLimit=10	   # 此配置表示,允许follower(相对于Leaderer言的“客户端”)连接并同步到Leader的初始化连接时间,以tickTime为单位。当初始化连接时间超过该值,则表示连接失败。
syncLimit=5		   # 此配置项表示Leader与Follower之间发送消息时,请求和应答时间长度。如果follower在设置时间内不能与leader通信,那么此follower将会被丢弃。
dataDir=/data/zookeeper/	# 运行数据的存放路径
dataLogdDir=/path1   # 指定事务日志的存储路径,可以和dataDir在不同设备,这意味着可以使用一个日志的专用磁盘,避免日志IO和快照竞争。
clientPort=2181
server.1=192.168.20.201:2888:38888
server.2=192.168.20.203:2888:38888
server.3=192.168.20.56:2888:38888
# server.myid=ip:leader_port:inner_port
# myid 为服务器编号,用于标识服务器,这个值必须和dataDir目录下myid文件中的值保证一致
# ip 为当前服务器IP,
# leader_port  Leader的端口
# inner_port  zk服务器之间内部通信端口
# 同一个集群内的服务器,需要把该集群内的服务器列表信息都写在配置文件中。

基本操作


zookeeper常用操作指令

conf: 输出相关服务配置的详细信息
cons: 列出所有连接到服务器的客户端的完全的连接会话的详细信息。包括“接受/发送”的包数量、会话id、操作延迟、最后的操作执行等信息
dump:列出未经处理的会话和临时节点
envi:输出关于服务环境的详细信息(区别conf命令)
reqs:列出未经处理的请求
ruok:测试服务是否处于正确状态。是返回“imok”,否则不做任何响应
stat:输出关于性能和连接的客户端列表
wchs:列出服务器watch的详细信息
wchc:通过session列出服务器的watch的详细信息,它的输出是一个与watch 相关的会话的列表
wchp:通过路径列出服务器watch的详细信息。它输出一个与session相关的路径。

客户端可以通过nctelnet连接ZooKeeper Server提交指令

简单示例:

#检查服务器状态是否正常
# echo ruok |nc localhost 2181
imok

#输出服务器配置信息
# echo conf |nc localhost 2181
clientPort=2181
dataDir=/data/zookeeper/version-2
dataLogDir=/data/zookeeper/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=10
syncLimit=5
electionAlg=3
electionPort=38888
quorumPort=2888
peerType=0

ZooKeeper Client 简单操作

create:创建Znode(父zonde必须存在)
delete:删除znode(zonde没有子节点)
exists:测试Znode是否存在,并获取它的元数据
getACL/setACL:为Znode获取/设置ACL
getData/setData:获取/设置Znode的相关数据
sync:使客户端的Znode视图与zookeeper同步

简单示例:

# 连接 ZooKeeper 服务器
bin/zkCli.sh -server localhost:2181
 
# 查看根下有哪些节点
[zk: localhost:2181(CONNECTED) 1] ls /
[controller_epoch, controller, brokers, zookeeper, admin, isr_change_notification, consumers, config]
 
# 查看 brokers 下有哪些子节点
[zk: localhost:2181(CONNECTED) 4] ls /brokers
[ids, topics, seqid]

# 在根下创建一个 "tuchao" 节点,并设置数据为 "hello zookeeper"
[zk: localhost:2181(CONNECTED) 5] create /tuchao "hello zookeeper"
 
# 查看是否创建成功
[zk: localhost:2181(CONNECTED) 6] ls / 
[controller_epoch, controller, brokers, zookeeper, tuchao, admin, isr_change_notification, consumers, config]
 
# 查看 /tuchao 中的数据 
[zk: localhost:2181(CONNECTED) 7] get /tuchao
hello zookeeper
cZxid = 0x20049ab24
ctime = Sun Oct 09 15:45:02 CST 2016
mZxid = 0x20049ab24
mtime = Sun Oct 09 15:45:02 CST 2016
pZxid = 0x20049ab24
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 15
numChildren = 0 
# 可以看到刚刚设置的数据。 

# 修改 /tuchao 中的数据为 "Happy birthday"
[zk: localhost:2181(CONNECTED) 8] set /tuchao "Happy birthday"
cZxid = 0x20049ab24
ctime = Sun Oct 09 15:45:02 CST 2016
mZxid = 0x20049b2cc
mtime = Sun Oct 09 15:51:17 CST 2016
pZxid = 0x20049ab24
cversion = 0
dataVersion = 1   # 可以发现,当数据变更时,数据版本号增加了。
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
 
# 再查看一次 /tuchao 中的数据
[zk: localhost:2181(CONNECTED) 9] get /tuchao
Happy birthday
cZxid = 0x20049ab24
ctime = Sun Oct 09 15:45:02 CST 2016
mZxid = 0x20049b2cc
mtime = Sun Oct 09 15:51:17 CST 2016
pZxid = 0x20049ab24
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
 
# 给 /tuchao 创建一个子节点 /tuchao/abc1 
[zk: localhost:2181(CONNECTED) 10] create /tuchao/abc1 "abc1 node"
Created /tuchao/abc1
[zk: localhost:2181(CONNECTED) 11] ls /tuchao  
[abc1]
 
# 删除节点 /tuchao/abc1
[zk: localhost:2181(CONNECTED) 0] delete /tuchao/abc1
[zk: localhost:2181(CONNECTED) 1] ls /tuchao
[]
 
# 查看命令帮助,输入 h 
[zk: localhost:2181(CONNECTED) 2] h
ZooKeeper -server host:port cmd args
	stat path [watch]
	set path data [version]
	ls path [watch]
	delquota [-n|-b] path
	ls2 path [watch]
	setAcl path acl
	setquota -n|-b val path
	history 
	redo cmdno
	printwatches on|off
	delete path [version]
	sync path
	listquota path
	rmr path
	get path [watch]
	create [-s] [-e] path data acl
	addauth scheme auth
	quit 
	getAcl path
	close 
	connect host:port

参考地址:http://blog.51cto.com/tchuairen/1859494

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值