首页 > 技术文章 > zookeeper学习

hujinzhong 2019-11-09 13:59 原文

一、zookeeper简介

官网地址:https://zookeeper.apache.org/

1.1、zookeeper概述

1、Zookeeper 是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性问题 ,例如怎样避免同时操作一数据造成脏读的问题 。

2、ZooKeeper 本质上是一个分布式的小文件存储系统 。提供基于类似文件系统的目录树方式数据存储 ,并且可以对树中的节点进行有效管理 。从而用来维 护和监控你存储的数据状态变化。通过这些,从而可以达到维护和监控你存储的数据状态变化。从而可以达到基于数据的集群管理 。诸如: 统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等

1.2、zookeeper特性

1、全局数据一致:集群中每个服务器保存一份相同的数据副本,client无论连接到哪个服务器 ,展示的数据都是一致,这是最重要特征;

2、可靠性:如果消息被其中一台服务器接受,那么江北所有的服务器接收

3、顺序性:包括全局有序和偏序。全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有的server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发布者发布,a必将排在b前面

4、数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态。

5、实时性:zookeeper保证客户端将在一个时间间隔内获取服务器的更新信息,或者服务器失效信息。

1.3、数据结构

zookeeper数据模型的结构与Unix文件系统很相似,整体可以看做一棵树,每一个节点称作为一个Znode,每一个znode默认能存储1MB的数据,每一个znode都可以通过其路径唯一标识。

1.4、应用场景

1.4.1、统一命名服务

1.4.2、统一配置管理

1.4.3、统一集群管理

1.4.4、服务器动态上下线

1.4.5、软负载均衡

二、zookeeper集群

2.1、本地模式部署安装

1)安装jdk

[root@zoo2 ~]# rpm -ivh jdk-8u181-linux-x64.rpm
[root@zoo2 ~]# java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

2)解压zookeeper安装包至指定目录

[root@zoo2 ~]# tar xf zookeeper-3.3.6.tar.gz -C /usr/local/
[root@zoo2 local]# mv /usr/local/zookeeper-3.3.6/ /usr/local/zookeeper

3)配置文件修改

[root@zoo2 conf]# cp zoo_sample.cfg zoo.cfg 
[root@zoo2 conf]# vim zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/data/zookeeper  #zookeeper数据存储目录,需要单独创建
# the port at which the clients will connect
clientPort=2181
[root@zoo2 conf]# mkdir -p /data/zookeeper

配置文件详细介绍:

#tickTime:通信心跳数,Zookeeper服务器心跳时间,单位毫秒
1、Zookeeper使用的基本时间,服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,时间单位为毫秒。 
2、它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间。(session的最小超时时间是2*tickTime) 

#initLimit:LF初始通信时限 
1、集群中的follower跟随者服务器(F)与leader领导者服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。 
2、投票选举新leader的初始化时间 
3、Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。 
4、Leader允许F在initLimit时间内完成这个工作。 

#syncLimit:LF同步通信时限
1、集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit * tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer。 
2、在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活
状态。 如果L发出心跳包在syncLimit之后,还没有从F那收到响应,那么就认为这个F已经不在线了。 

#dataDir:数据文件目录+数据持久化路径
保存内存数据库快照信息的位置,如果没有其他说明,更新的事务日志也保存到数据库

#clientPort
监听客户端连接的端口

4)启动zookeeper并查看状态

[root@zoo2 conf]# /usr/local/zookeeper/bin/zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@zoo2 conf]# jps
4121 QuorumPeerMain
4141 Jps
[root@zoo2 conf]# /usr/local/zookeeper/bin/zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: standalone
[root@zoo2 conf]# netstat -lntp|grep 2181
tcp6       0      0 :::2181                 :::*                    LISTEN      4295/java

#zookeeper停止命令
[root@zoo2 conf]# /usr/local/zookeeper/bin/zkServer.sh stop

#启动客户端
[root@zoo2 conf]# /usr/local/zookeeper/bin/zkCli.sh  #不需要start
[zk: localhost:2181(CONNECTED) 0]  #quit退出

2.2、集群角色

2.2.1、Leader

1)Zookeeper集群工作的核心

2)事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性;

3)集群内部各个服务器的调度者

4)对于create,setData,delete等有写操作的请求,则需要统一转发给leader处理,leader需要决定编号、执行操作,这个过程称为一个事务。

2.2.2、Follower

1)处理客户端非事务(读操作)请求,转发事务请求给Leader

2)参与集群Leader选举投票。

2.1.3、Observer

针对访问量比较大的zookeeper集群,还可新增观察者角色

1)观察者角色,观察Zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给Leader服务器进行处理

2)不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力

2.3、选举机制

1)半数机制(Paxos 协议):集群中半数以上机器存活,集群可用。所以zookeeper适合装在奇数台机器上

2)Zookeeper 虽然在配置文件中并没有指定master 和slave。但是,zookeeper 工作时,是有一个节点为leader,其他则为follower,Leader 是通过内部的选举机制临时产生的

3)以一个简单的例子来说明整个选举的过程。

假设有五台服务器组成的zookeeper 集群,它们的id 从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么

(1)服务器1 启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING 状态。
(2)服务器2 启动,它与最开始启动的服务器1 进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id 值较大服务器2 胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中半数以上是3),所以服务器1、2 还是继续保持LOOKING 状态。
(3)服务器3 启动,根据前面的理论分析,服务器3 成为服务器1、2、3 中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader。
(4)服务器4 启动,根据前面的分析,理论上服务器4 应该是服务器1、2、3、4 中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的了。
(5)服务器5 启动,同4 一样当小弟。

2.4、节点类型

image

2.4、集群搭建

2.4.1、搭建注意事项

1)Zookeeper集群搭建指的是ZooKeeper分布式模式安装。通常由2n+1台servers组成。这是因为为了保证Leader选举(基于Paxos算法的实现)能过得到多数的支持,所以ZooKeeper集群的数量一般为奇数

2)Zookeeper运行需要java环境,所以需要提前安装jdk。对于安装leader+follower模式的集群,大致过程如下:

  1. 配置主机名称到IP地址映射配置(也可以直接在zookeeper文件中ongoing写ip地址)
  2. 修改ZooKeeper配置文件
  3. 远程复制分发安装文件
  4. 设置myid
  5. 启动ZooKeeper集群

3)如果要想使用 Observer 模式,可在对应节点的配置文件中添加如下配置

peerType=observer

其次,必须在配置文件指定哪些节点被为 Observer ,如 :

server.1:localhost:2181:3181:observer

2.4.2、搭建过程记录

1)集群规划

主机 ip地址
zoo1 10.0.0.202
zoo2 10.0.0.203
zoo3 10.0.0.204

2)三台分别安装zookeeper

见本地模式安装

3)创建myid文件

#三台不一样
[root@zoo1 ~]# echo 1 > /data/zookeeper/myid
[root@zoo2 ~]# echo 2 > /data/zookeeper/myid
[root@zoo3 ~]# echo 3 > /data/zookeeper/myid

4)修改配置文件

#三台要一样
[root@zoo2 ~]# cat /usr/local/zookeeper/conf/zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/data/zookeeper
# the port at which the clients will connect
clientPort=2181

#cluster
server.1=10.0.0.202:2888:3888
server.2=10.0.0.203:2888:3888
server.3=10.0.0.204:2888:3888

配置参数说明:

Server.A=B:C:D

A是一个数字,表示这个是第几号服务器;集群模式下配置一个文件myid,这个文件在dataDir目录下,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。
B是这个服务器的ip地址;
C是这个服务器与集群中的Leader服务器交换信息的端口;
D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。

5)启动zookeeper

[root@zoo1 conf]# /usr/local/zookeeper/bin/zkServer.sh start
[root@zoo2 conf]# /usr/local/zookeeper/bin/zkServer.sh start
[root@zoo3 conf]# /usr/local/zookeeper/bin/zkServer.sh start

#查看状态
[root@zoo1 conf]# /usr/local/zookeeper/bin/zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: follower

[root@zoo2 conf]# /usr/local/zookeeper/bin/zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: leader

[root@zoo3 zookeeper]# /usr/local/zookeeper/bin/zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: follower

三、客户端命令行操作

image

3.1、启动客户端

[root@zoo2 zookeeper]# bin/zkCli.sh 
Connecting to localhost:2181
2019-11-10 21:00:01,289 - INFO  [main:Environment@97] - Client environment:zookeeper.version=3.3.6-1366786, built on 07/29/2012 06:22 GMT
2019-11-10 21:00:01,298 - INFO  [main:Environment@97] - Client environment:host.name=slave01
2019-11-10 21:00:01,299 - INFO  [main:Environment@97] - Client environment:java.version=1.8.0_181
2019-11-10 21:00:01,300 - INFO  [main:Environment@97] - Client environment:java.vendor=Oracle Corporation
2019-11-10 21:00:01,300 - INFO  [main:Environment@97] - Client environment:java.home=/usr/java/jdk1.8.0_181-amd64/jre
2019-11-10 21:00:01,301 - INFO  [main:Environment@97] - Client environment:java.class.path=/usr/local/zookeeper/bin/../build/classes:/usr/local/zookeeper/bin/../build/lib/*.jar:/usr/local/zookeeper/bin/../zookeeper-3.3.6.jar:/usr/local/zookeeper/bin/../lib/log4j-1.2.15.jar:/usr/local/zookeeper/bin/../lib/jline-0.9.94.jar:/usr/local/zookeeper/bin/../src/java/lib/*.jar:/usr/local/zookeeper/bin/../conf:
2019-11-10 21:00:01,304 - INFO  [main:Environment@97] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2019-11-10 21:00:01,304 - INFO  [main:Environment@97] - Client environment:java.io.tmpdir=/tmp
2019-11-10 21:00:01,305 - INFO  [main:Environment@97] - Client environment:java.compiler=<NA>
2019-11-10 21:00:01,306 - INFO  [main:Environment@97] - Client environment:os.name=Linux
2019-11-10 21:00:01,307 - INFO  [main:Environment@97] - Client environment:os.arch=amd64
2019-11-10 21:00:01,308 - INFO  [main:Environment@97] - Client environment:os.version=3.10.0-693.el7.x86_64
2019-11-10 21:00:01,309 - INFO  [main:Environment@97] - Client environment:user.name=root
2019-11-10 21:00:01,309 - INFO  [main:Environment@97] - Client environment:user.home=/root
2019-11-10 21:00:01,310 - INFO  [main:Environment@97] - Client environment:user.dir=/usr/local/zookeeper
2019-11-10 21:00:01,333 - INFO  [main:ZooKeeper@379] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@2e817b38
2019-11-10 21:00:01,370 - INFO  [main-SendThread():ClientCnxn$SendThread@1058] - Opening socket connection to server localhost/127.0.0.1:2181
Welcome to ZooKeeper!
JLine support is enabled
2019-11-10 21:00:01,715 - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@947] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-11-10 21:00:01,790 - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@736] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x26e555182ee0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]

4.2、显示所有操作命令

[zk: localhost:2181(CONNECTED) 0] help
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
	get path [watch]
	create [-s] [-e] path data acl
	addauth scheme auth
	quit 
	getAcl path
	close 
	connect host:port
[zk: localhost:2181(CONNECTED) 1]

4.3、查看当前节点信息

[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls2 /   #查看详细数据
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1

4.4、创建普通节点

[zk: localhost:2181(CONNECTED) 3] create /sanguo "jinlian"
Created /sanguo
[zk: localhost:2181(CONNECTED) 4] ls /                    
[zookeeper, sanguo]
[zk: localhost:2181(CONNECTED) 5] create /sanguo/shuguo "liubei"
Created /sanguo/shuguo
[zk: localhost:2181(CONNECTED) 7] ls /sanguo
[shuguo]
[zk: localhost:2181(CONNECTED) 8] get /sanguo/shuguo
"liubei"
cZxid = 0x100000003
ctime = Sun Nov 10 21:05:18 CST 2019
mZxid = 0x100000003
mtime = Sun Nov 10 21:05:18 CST 2019
pZxid = 0x100000003
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0

#创建短暂节点(当前连接可以看到,退出当前客户端后在启动客户端后,短暂节点消失)
[zk: localhost:2181(CONNECTED) 10] create -e /sanguo/wuguo "sunquan"
Created /sanguo/wuguo
[zk: localhost:2181(CONNECTED) 11] ls /sanguo
[wuguo, shuguo]
[zk: localhost:2181(CONNECTED) 12] quit
[root@zoo2 zookeeper]# /usr/local/zookeeper/bin/zkCli.sh
[zk: localhost:2181(CONNECTED) 0] ls /sanguo
[shuguo]

#创建带序号的是节点
[zk: localhost:2181(CONNECTED) 1] create -s /sanguo/weiguo "caocao"
Created /sanguo/weiguo0000000003
[zk: localhost:2181(CONNECTED) 2] ls /sanguo
[weiguo0000000003, shuguo]

4.5、修改节点数据值

[zk: localhost:2181(CONNECTED) 8] set /sanguo/shuguo "zhugeliang"
cZxid = 0x100000003
ctime = Sun Nov 10 21:05:18 CST 2019
mZxid = 0x10000000a
mtime = Sun Nov 10 21:25:44 CST 2019
pZxid = 0x100000003
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0
[zk: localhost:2181(CONNECTED) 9] get /sanguo/shuguo             
"zhugeliang"
cZxid = 0x100000003
ctime = Sun Nov 10 21:05:18 CST 2019
mZxid = 0x10000000a
mtime = Sun Nov 10 21:25:44 CST 2019
pZxid = 0x100000003
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0

4.6、节点值变化监听

1、节点值变化监听
#在zoo1主机上注册监听/sanguo节点数据变化
[zk: localhost:2181(CONNECTED) 0] get /sanguo watch

#在zoo2上修改/sanguo节点的数据
[zk: localhost:2181(CONNECTED) 10] set /sanguo "xisi"

#观察zoo1主机收到的数据变化的监听
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/sanguo
------------------------------------------------------------------------

2、节点的子节点变化监听(路径变化)
#在zoo1上注册监听/sanguo节点的子节点变化
[zk: localhost:2181(CONNECTED) 1] ls /sanguo watch

#在zoo2的节点/sanguo上创建子节点
[zk: localhost:2181(CONNECTED) 11] create /sanguo/jin "simayi"
Created /sanguo/jin

#观察zoo1收到的子节点变化监听
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/sanguo

4.7、删除节点

[zk: localhost:2181(CONNECTED) 14] delete /sanguo/jin

4.8、查看节点状态

[zk: localhost:2181(CONNECTED) 21] stat /sanguo 
cZxid = 0x100000002
ctime = Sun Nov 10 21:04:02 CST 2019
mZxid = 0x10000000c
mtime = Sun Nov 10 21:29:31 CST 2019
pZxid = 0x10000000e
cversion = 6
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 2
---------------------------------------------------

1)czxid
引起这个znode创建的zxid,创建节点的事务的zxid,每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
2)ctime 
znode被创建的毫秒数(从1970年开始)
3)mzxid 
znode最后更新的zxid
4)mtime 
znode最后修改的毫秒数(从1970年开始)
5)pZxid
znode最后更新的子节点zxid
6)cversion 
znode子节点变化号,znode子节点修改次数
7)dataversion 
znode数据变化号
8)aclVersion 
znode访问控制列表的变化号
9)ephemeralOwner
如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
10)dataLength
znode的数据长度
11)numChildren 
znode子节点数量

五、监听器原理

image

六、写数据流程

image

七、API应用

待续

推荐阅读