目标
- 什么是分布式
- 为什么选择Zookeeper
- Zookeeper的设计目标
- Zookeeper的数据模型
- 安装Zookeeper
- 单节点配置Zookeeper
- 多节点配置Zookeeper
- Zookeeper的常用命令
- Zookeeper的监听
- Zookeeper四字命令
什么是分布式
分布式系统
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
分布式协调技术
分布式系统的出现带来了分布式协调的问题,也就是我们如何对分布式系统中的进程进行调度,假设我们在某一台机器上面挂载了一个资源,然后其他物理分布的进程都要竞争这个资源,但是我们又不希望他们同时访问,这时候就需要一个协调器,让他们有序的来访问在这个资源。这个协调器就是我们经常提到的那个锁,比如说”进程-1”在使用该资源的时候,会先去获得锁,”进程1”获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,”进程1”用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。分布式锁也就是我们分布式协调技术实现的核心内容。
为什么选择zookeeper
ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务。由于ZooKeeper的开源特性,后来我们的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。
ZooKeeper在实现这些服务时,首先它设计一种新的数据结构——Znode,然后在该数据结构的基础上定义了一些原语,也就是一些关于该数据结构的一些操作。有了这些数据结构和原语还不够,因为我们的ZooKeeper是工作在一个分布式的环境下,我们的服务是通过消息以网络的形式发送给我们的分布式应用程序,所以还需要一个通知机制——Watcher机制。那么总结一下,ZooKeeper所提供的服务主要是通过:数据结构+原语+watcher机制,三个部分来实现的。
ZooKeeper性能上的特点决定了它能够用在大型的、分布式的系统当中。从可靠性方面来说,它并不会因为一个节点的错误而崩溃。除此之外,它严格的序列访问控制意味着复杂的控制原语可以应用在客户端上。ZooKeeper在一致性、可用性、容错性的保证,也是ZooKeeper的成功之处,它获得的一切成功都与它采用的协议——Zab协议是密不可分的,这些内容将会在后面介绍。
Zookeeper的设计目标
ZooKeeper 很简单。ZooKeeper允许分布式进程通过共享的层次命名空间相互协调,该命名空间的组织类似于标准文件系统。名称空间由数据寄存器组成——用ZooKeeper的说法称为znodes——这些寄存器类似于文件和目录。与典型的用于存储的文件系统不同,ZooKeeper数据保存在内存中,这意味着ZooKeeper可以实现高吞吐量和低延迟数。
ZooKeeper实现非常重视高性能、高可用性和严格有序的访问。ZooKeeper的性能方面意味着它可以用于大型分布式系统。可靠性方面使它不会成为单点故障。严格的排序意味着复杂的同步原语可以在客户端实现。
ZooKeeper 是复制。与它所协调的分布式进程一样,ZooKeeper本身也打算在一组称为集合的主机上进行复制。
组成ZooKeeper服务的服务器必须相互了解。它们在内存中维护状态映像,以及持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper服务就可用。
客户端连接到单个ZooKeeper服务器。客户端维护一个TCP连接,通过它发送请求、获取响应、获取监视事件和发送心跳。如果到服务器的TCP连接中断,客户机将连接到另一台服务器。
ZooKeeper是有序的。ZooKeeper用一个数字来标记每个更新,这个数字反映了所有ZooKeeper事务的顺序。后续操作可以使用该顺序来实现更高级别的抽象,比如同步原语。
ZooKeeper很快。在“以读取为主”的工作负载中,它的速度特别快。ZooKeeper应用程序运行在数千台机器上,当读操作比写操作更常见时,它的性能最好,比率约为10:1。
Zookeeper的数据模型
ZooKeeper拥有一个层次的命名空间,这个和标准的文件系统非常相似,如下图所示。
从图中我们可以看出ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,都是采用这种树形层次结构,ZooKeeper树中的每个节点被称为—Znode。和文件系统的目录树一样,ZooKeeper树中的每个节点可以拥有子节点。但也有不同之处:
引用方式
Zonde通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串”/zookeeper”用以保存管理信息,比如关键配额信息。
Znode结构
ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个Znode。 每个Znode由3部分组成:
stat:此为状态信息, 描述该Znode的版本, 权限等信息
data:与该Znode关联的数据
children:该Znode下的子节点
ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,但常规使用中应该远小于此值。
数据访问
ZooKeeper中的每个节点存储的数据要被原子性的操作。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。
节点类型
ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
临时节点:
- 该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。
永久节点:
- 该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
顺序节点
当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为”%10d”(10位数字,没有数值的数位用0补充,例如”0000000001”)。当计数值大于232-1时,计数器将溢出。
观察
客户端可以在节点上设置watch,我们称之为监视器。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次,这样可以减少网络流量。
安装Zookeeper
下载源码包()
http://archive.cloudera.com/cdh5/cdh/5/zookeeper-3.4.5-cdh5.16.2.tar.gz
编译
yum install ant //zk的编译不是使用maven的,而是使用ant
ant package需要注意两个坑:
- 需要指定jdk为1.7
- 远端仓库连不上,需要改路径
配置环境变量
单节点配置Zookeeper
- 修改zoo.cfg文件(需要从zoo_sample.cfg复制一份)
- 修改dataDir路径,默认放在/tmp目录下,dataDir主要用于保存Zookeeper中的数据
- 启动服务端
bin/zkServer.sh start
- 启动客户端
bin/zkCli.sh
多节点配置Zookeeper
修改zoo.cfg文件(需要从zoo_sample.cfg复制一份)
修改dataDir路径,默认放在/tmp目录下,dataDir主要用于保存Zookeeper中的数据
在zoo.cfg文件中配置集群的id、主机和端口号,
集群共享内容
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888server.A=B:C:D
A:标识第几号服务器,有一个myid要与之对应
B:服务器的地址
C:follower与leader通信的端口
D:选举leader时的端口在dataDir指定的路径下创建myid文件,添加与之对应的服务器编号,即编号A,
集群唯一内容
,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。启动集群服务端
bin/zkServer.sh start
启动集群客户端 ,工作中客户端连接集群命令:
./zkCli.sh -server hadoop102:2181,hadoop103:2181,hadoop104:2181
Zookeeper的常用命令
stat 状态 |
stat path [watch]
:数据的状态[zk: localhost:2181(CONNECTED) 1] stat /tunan
cZxid = 0x21 //节点的id号
ctime = Mon Mar 02 15:27:36 CST 2020 //节点的创建时间
mZxid = 0x32 //节点修改的id号
mtime = Mon Mar 02 15:36:45 CST 2020 //节点修改的时间
pZxid = 0x38 //最后更新的子节点的id
cversion = 9 //节点所拥有的子节点的被修改的版本号
dataVersion = 1 //节点数据版本号
aclVersion = 0 //节点所拥有的ACL版本号
ephemeralOwner = 0x0 //特殊标记 0x0: 永久节点
dataLength = 3 //数据的长度
numChildren = 5 //子节点的个数set path data [version]
:修改数据[zk: localhost:2181(CONNECTED) 5] set /tunan/aaa 123456
cZxid = 0x31 //这是一个子节点的id跟上面的父节点的id不同
ctime = Mon Mar 02 15:35:31 CST 2020 //子节点的创建时间
mZxid = 0x3a //子节点的修改id
mtime = Tue Mar 03 11:28:06 CST 2020 //子节点的修改时间
pZxid = 0x31 //最后更新的子节点的id,我们现在更新的是这个节点,所以对应cZxid
cversion = 0 //节点所拥有的子节点的被修改的版本号,明显和父节点不同
dataVersion = 1 //数据被修改过了,所以版本号变了
aclVersion = 0 //节点所拥有的ACL版本号
ephemeralOwner = 0x0 //特殊标记 0x0: 永久节点
dataLength = 6 //数据的长度
numChildren = 0 //子节点的个数ls path [watch]
:查看节点[zk: localhost:2181(CONNECTED) 8] ls /tunan
[aaa, ccc, bbb, eee, ddd]
ls2 path [watch]
:查看并获取节点内容,等同于get[zk: localhost:2181(CONNECTED) 9] ls2 /tunan
[aaa, ccc, bbb, eee, ddd] //节点的数据内容
delete path [version]
:删除节点[zk: localhost:2181(CONNECTED) 10] delete /tunan/aaa
[zk: localhost:2181(CONNECTED) 11] ls /tunan
[ccc, bbb, eee, ddd]
rmr path
:递归删除节点[zk: localhost:2181(CONNECTED) 12] rmr /tunan/ccc
[zk: localhost:2181(CONNECTED) 13] ls /tunan
[bbb, eee, ddd]get path [watch]
:获取节点内容,等同于ls2[zk: localhost:2181(CONNECTED) 14] get /tunan/bbb
222
create [-s] [-e] path data acl
:创建节点-s:顺序创建
[zk: localhost:2181(CONNECTED) 0] create -s /tunan/bbb 12345
Created /tunan/bbb0000000007
[zk: localhost:2181(CONNECTED) 1] create -s /tunan/bbb 12345
Created /tunan/bbb0000000008
-e:临时创建
[zk: localhost:2181(CONNECTED) 5] create -e /tunan/fff 12345
Created /tunan/fff
Zookeeper的监听
watch概述
ZooKeeper可以为所有的读操作设置watch,这些读操作包括:exists()、getChildren()及getData()。watch事件是一次性的触发器,当watch的对象状态发生改变时,将会触发此对象上watch所对应的事件。watch事件将被异步地发送给客户端,并且ZooKeeper为watch机制提供了有序的一致性保证。理论上,客户端接收watch事件的时间要快于其看到watch对象状态变化的时间。
watch类型
ZooKeeper所管理的watch可以分为两类:
数据watch(data watches):getData和exists负责设置数据watch
孩子watch(child watches):getChildren负责设置孩子watch
我们可以通过操作返回的数据来设置不同的watch:
- getData和exists:
- getChildren:返回孩子列表
因此
一个成功的setData操作将触发Znode的数据watch
一个成功的create操作将触发Znode的数据watch以及孩子watch
一个成功的delete操作将触发Znode的数据watch以及孩子watch
watch注册与处触发
图 6.1 watch设置操作及相应的触发器如图下图所示:
- exists操作上的watch,在被监视的Znode创建、删除或数据更新时被触发。
- getData操作上的watch,在被监视的Znode删除或数据更新时被触发。在被创建时不能被触发,因为只有
- getChildren操作上的watch,在被监视的Znode的子节点创建或删除,或是这个Znode自身被删除时被触发。可以通过查看watch事件类型来区分是Znode,还是他的子节点被删除:NodeDelete表示Znode被删除,NodeDeletedChanged表示子节点被删除。
Watch由客户端所连接的ZooKeeper服务器在本地维护,因此watch可以非常容易地设置、管理和分派。当客户端连接到一个新的服务器时,任何的会话事件都将可能触发watch。另外,当从服务器断开连接的时候,watch将不会被接收。但是,当一个客户端重新建立连接的时候,任何先前注册过的watch都会被重新注册。
需要注意的几点
Zookeeper的watch实际上要处理两类事件:
连接状态事件(type=None, path=null)
这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。
节点事件
节点的建立,删除,数据的修改。它是one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。
上面2类事件都在Watch中处理,也就是重载的process(Event event)
节点事件的触发,通过函数exists,getData或getChildren来处理这类函数,有双重作用:
- 注册触发事件
- 函数本身的功能
函数的本身的功能又可以用异步的回调函数来实现,重载processResult()过程中处理函数本身的的功能。
命令行操作
监听创建节点(
stat命令
)stat /xiaoqi watch
[zk: localhost:2181(CONNECTED) 13] create /xiaoqi 111
WATCHER::
WatchedEvent state:SyncConnected type:NodeCreated path:/xiaoqi
Created /xiaoqi监听修改节点(
stat命令
)stat /xiaoqi watch
[zk: localhost:2181(CONNECTED) 16] set /xiaoqi 1234
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/xiaoqi监听删除节点(
stat命令
)stat /xiaoqi watch
[zk: localhost:2181(CONNECTED) 20] delete /xiaoqi
WATCHER::
WatchedEvent state:SyncConnected type:NodeDeleted path:/xiaoqi监听父节点删除子节点(
ls命令
)ls /xiaoqi watch
[zk: localhost:2181(CONNECTED) 29] create /xiaoqi/aaa 111
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/xiaoqi监听父节点创建子节点(
ls命令
)ls /xiaoqi watch
[zk: localhost:2181(CONNECTED) 31] delete /xiaoqi/aaa
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/xiaoqi
Zookeeper四字命令
无须启动zk客户端,直接在命令行输入echo {}| nc localhost 2181
即可
stat:
列出服务器和连接的客户机的简要信息。
echo stat | nc localhost 2181
Zookeeper version: 3.4.5-cdh5.16.2--1, built on 06/03/2019 10:40 GMT |
ruok:
测试服务器是否在非错误状态下运行。
echo ruok | nc localhost 2181
imok |
dump:
列出未完成的会话和临时节点。这只对leader有效。
echo dump | nc localhost 2181
SessionTracker dump: |
conf:
打印关于服务配置的详细信息。
echo conf | nc localhost 2181
clientPort=2181 //端口 |
envi:
打印出服务环境的细节
echo envi | nc localhost 2181
Environment: |
mntr:
输出可用于监视集群健康状况的变量列表。
echo mntr | nc localhost 2181
zk_version 3.4.5-cdh5.16.2--1, built on 06/03/2019 10:40 GMT |
wchs:
按会话列出关于服务器监视的详细信息。
echo wchs | nc localhost 2181
1 connections watching 1 paths |