【老哥我最近接到个任务研究一下Zookeeper,对于我这个Linux运维领域的小菜鸟来讲也是刚刚听到这个名字,为了养成良好的文档整理和学习能力,我人生第一次开通了博客并把此次的研究经历记录了下来,之后我会不按期的记录下来我对技术领域的探索,但愿热爱Linux运维志同道合的兄弟们多指教,一同进步成长。
(ps:我本人平时比较沉默,善于观察思考,对历史人物很有看法,可是一旦提及话来就会口若悬河,谁让我曾经的梦想是当一名教师呢!哈哈!)
同时,送给你们一句话,人生是一场马拉松比赛,只有坚持到最后的人,才会让人肃然起敬,并非由于他得到多大的成就,而是那知其不可为而为之的精神。】node
言归正传,学习一种新的技术首先固然是要拜访Zookeeper的官方网站:http://zookeeper.apache.org/
软件也是一种产品,而它的官方网站就是它的说明书,在这里你能找到你所想要的东西。linux
分布式系统
分布式系统是由独立的计算机经过网络链接在一块儿,而且经过一些组件来相互交流和协做来完成一个共同的目标。nginx
Zookeeper简介
ZooKeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步和提供组服务。全部这些服务都以某种形式由分布式应用程序使用。每次实现它们时,都须要作大量工做来修复不可避免的bug和竞争条件。因为难以实现这些服务,应用程序最初一般会节省这些服务,这使得它们在出现更改时变得脆弱,而且难以管理。即便处理正确,这些服务的不一样实如今部署应用程序时也会致使管理复杂性。为了解决这一问题,开发人员就研发出了Zookeeper。redis
1、Zookeeeper单机安装及简单操做
从https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/ 下载软件
[root@8c649f93f3bc soft]# wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/
[root@8c649f93f3bc soft]# ls
Django-2.1.11 jdk-8u162-linux-x64.rpm redis-4.0.9
Django-2.1.11.tar.gz nginx-1.13.12 redis-4.0.9.tar.gz
install.sh nginx-1.13.12.tar.gz zookeeper-3.4.14.tar.gz算法
解压软件包,解压后会生成一个zookeeper-3.4.14的目录,进入目录就会看到以下内容:
[root@8c649f93f3bc zookeeper-3.4.14]# ls
bin README.md zookeeper-client
build.xml README_packaging.txt zookeeper-contrib
conf src zookeeper-docs
dist-maven zoo1.cfg zookeeper-it
ivysettings.xml zoo2.cfg zookeeper-jute
ivy.xml zoo3.cfg zookeeper.out
lib zookeeper-3.4.14.jar zookeeper-recipes
LICENSE.txt zookeeper-3.4.14.jar.asc zookeeper-server
NOTICE.txt zookeeper-3.4.14.jar.md5
pom.xml zookeeper-3.4.14.jar.sha1数据库
要启动Zookeeper须要一个配置文件,建立配置文件conf/zoo.cfg
[root@8c649f93f3bc zookeeper-3.4.14]# cat conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper
clientPort=2181
下面是每一个字段的含义:
tickTime 单位为微秒,用于session注册和客户端和ZooKeeper服务的心跳周期。session超时时长最小为 tickTime的两倍
dataDir :存储内存中数据库快照的位置,以及更新到数据库的事务日志(除非另有指定)。
clientPort: 监听客户机链接的端口,默认是2181express
启动Zookeeper,因为个人配置文件的相对路径比较长,因此我对其作了个符号链接,以便操做
[root@8c649f93f3bc zookeeper-3.4.14]# ln -s conf/zoo.cfg zoo.cfg
此时就能够指定配置文件启动Zookeeper
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh start zoo.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo.cfg
Starting zookeeper ... already running as process 11614.
看到上述描述,就表示Zookeeper启动成功。apache
查看Zookeeper的状态
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh status zoo.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: standalone
这里能够看到ZK的运行模式为独立的。缓存
对ZK进行管理,链接到zookeeper
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkCli.sh -server 127.0.0.1:2181
Connecting to 127.0.0.1:2181
敲一下help查看一下支持的命令
[zk: 127.0.0.1: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
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port安全
经过运行create /zk_test my_data建立一个新的znode,这将建立一个新的znode并将字符串“my_data”与该节点关联。
[zk: 127.0.0.1:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 2] create /zk_test my_data
Created /zk_test
[zk: 127.0.0.1:2181(CONNECTED) 4] ls /
[zookeeper, zk_test]
接下来,运行get命令验证数据是否与znode关联,以下所示:
[zk: 127.0.0.1:2181(CONNECTED) 5] get /zk_test
my_data
cZxid = 0x6
ctime = Tue Aug 20 05:29:52 UTC 2019
mZxid = 0x6
mtime = Tue Aug 20 05:29:52 UTC 2019
pZxid = 0x6
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
也能够经过发出set命令来更改与zk_test关联的数据
[zk: 127.0.0.1:2181(CONNECTED) 6] set /zk_test junk
cZxid = 0x6
ctime = Tue Aug 20 05:29:52 UTC 2019
mZxid = 0x7
mtime = Tue Aug 20 05:37:26 UTC 2019
pZxid = 0x6
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: 127.0.0.1:2181(CONNECTED) 7] get /zk_test
junk
cZxid = 0x6
ctime = Tue Aug 20 05:29:52 UTC 2019
mZxid = 0x7
mtime = Tue Aug 20 05:37:26 UTC 2019
pZxid = 0x6
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
最后,咱们能够用以下命令删除node
[zk: 127.0.0.1:2181(CONNECTED) 2] ls /
[zookeeper, zk_test]
[zk: 127.0.0.1:2181(CONNECTED) 3] delete /zk_test
[zk: 127.0.0.1:2181(CONNECTED) 4] ls /
[zookeeper]
2、了解了Zookeeper的单机安装,下面咱们来了解一下Zookeeper的基本概念:
Zookeeper的数据模型
ZooKeeper有一个分层的名称空间,很像一个分布式文件系统。唯一的区别是名称空间中的每一个节点均可以拥有与其关联的数据和子节点。这就像一个文件系统容许一个文件同时也是一个目录。节点的路径老是表示为规范的、绝对的、斜杠分隔的路径;任何unicode字符都可在符合下列限制的路径中使用:
1.null字符(\u0000)不能做为路径名的一部分。(这会致使C绑定出现问题。)
2.如下字符不能使用,由于它们不能很好地显示,或者以使人困惑的方式呈现:\u0001 - \u001F和\u007F
3.\ u009F。
4.不容许使用如下字符:\ud800 - uF8FF, \uFFF0 - uFFFF。
5.“.”字符能够用做另外一个名称的一部分,可是“.”和“..”不能单独用于指示路径上的节点,由于ZooKeeper不使用相对路径。下列内容无效:“/a/b/”。/ c”或“c / a / b / . . /”。
6.象征性的“zookeeper”被保留。
ZNodes
ZooKeeper树中的每一个节点都被称为znode。Znodes维护一个stat结构,其中包含数据更改、acl更改的版本号。stat结构还具备时间戳。版本号和时间戳容许ZooKeeper验证缓存并协调更新。每次znode的数据发生变化,版本号就会增长。例如,每当客户机检索数据时,它也接收数据的版本。当客户机执行更新或删除操做时,它必须提供正在更改的znode数据的版本。若是它提供的版本与数据的实际版本不匹配,更新将失败。
(在分布式应用工程中,节点能够指通常的主机、服务器、集成的成员、客户机进程等。在ZooKeeper文档中,znodes指的是数据节点。服务器是指构成Zookeeper服务的机器;仲裁对等点是指组成合集的服务器;客户端是指任何使用ZooKeeper服务的主机或进程。)
znode有以下几个特征:
Time in ZooKeeper
1.zxid 对ZooKeeper状态的每次更改都会收到一个zxid (ZooKeeper事务Id)形式的戳记。这将向ZooKeeper公开全部更改的总顺序。每一个更改都有一个唯一的zxid,若是zxid1小于zxid2,则zxid1发生在zxid2以前。
2.Version numbers 对节点的每一次更改都会致使该节点的版本号之一增长。这三个版本号是version(对znode数据的更改数量)、cversion(对znode子节点的更改数量)和hate(对znode ACL的更改数量)。
3.Ticks 当使用多服务器ZooKeeper时,服务器使用刻度来定义事件的时间,例如状态上传、会话超时、对等点之间的链接超时等。滴答时间仅经过最小会话超时(滴答时间的2倍)间接公开;若是客户机请求的会话超时小于最小会话超时,服务器将告诉客户机会话超时其实是最小会话超时。
4.Real time ZooKeeper根本不使用实时或时钟时间,除了在znode建立和修改时将时间戳放入stat结构以外。
ZooKeeper Stat Structure
ZooKeeper中每一个znode的统计结构由如下字段组成:
1.czxid zxid的变化会致使这个节点被建立
2.mzxid 最后修改的zxid
3.pzxid 最后修改zxid的子节点
4.ctime 从建立znode开始的时间(以毫秒为单位)。
5.mtime 此znode最后一次修改的时间(以毫秒为单位)。
6.version 此znode的数据的更改数。
7.cversion 此znode的子节点的更改数。
8.aversion 更改此znode的ACL的次数。
9.ephemeralOwner 若是znode是临时节点,则此znode全部者的会话id。若是不是临时节点,则为零。
10.dataLength 此znode的数据字段的长度。
11.numChildren 这个znode的子节点的个数。
ZooKeeper Sessions
ZooKeeper客户端经过使用语言绑定建立服务句柄来与ZooKeeper服务创建会话。建立好句柄后,句柄将以链接状态启动,客户端库将尝试链接到构成ZooKeeper服务的服务器之一,此时它将切换到链接状态。在正常操做期间,客户端句柄将处于这两种状态之一。若是出现不可恢复的错误,好比会话过时或身份验证失败,或者应用程序显式关闭句柄,句柄将移动到关闭状态。要建立一个客户端会话,应用程序代码必须提供一个链接字符串,其中包含一个逗号分隔的host:port对列表,每一个host:port对对应于ZooKeeper服务器(例如“127.0.0.1:4545”或“127.0.0.1:3000、127.0.0.1:3001 127.0.0.1:3002”)。ZooKeeper客户端库将选择一个任意的服务器并尝试链接到它。若是这个链接失败,或者客户机因为任何缘由与服务器断开链接,客户机将自动尝试列表中的下一个服务器,直到(从新)创建链接。在3.2.0版本后,还能够在链接字符串后面附加一个可选的“chroot”后缀。这将运行客户机命令,同时解释与此根相关的全部路径(相似于unix chroot命令)。若是使用示例的样子:“127.0.0.1:4545 / app /”或“127.0.0.1:3000、127.0.0.1:3001 127.0.0.1:3002 / app /“客户端将扎根在/ app /和全部路径是相对于这个根——即root - ie getting/setting/etc... "/foo/bar"将致使在“/app/a/foo/bar”上运行操做(从服务器的角度来看)。这个特性在多租户环境中特别有用,在多租户环境中,特定ZooKeeper服务的每一个用户均可以有不一样的根。这使得重用更加简单,由于每一个用户均可以编写他/她的应用程序,就像它植根于“/”同样,而实际位置(好比/app/a)能够在部署时肯定。
当客户端得到ZooKeeper服务的句柄时,ZooKeeper建立一个ZooKeeper会话(表示为64位数字),并将其分配给客户端。若是客户机链接到另外一个ZooKeeper服务器,它将发送会话id做为链接握手的一部分。做为安全措施,服务器为任何ZooKeeper服务器均可以验证的会话id建立一个密码。当客户端创建会话时,密码将与会话id一块儿发送给客户端。每当客户机使用新服务器从新创建会话时,它就用会话id发送这个密码。
建立ZooKeeper会话的ZooKeeper客户端库调用的参数之一是会话超时(以毫秒为单位)。客户机发送一个请求的超时,服务器用它能够给客户机的超时进行响应。当前实现要求超时时间最少为tickTime的2倍(在服务器配置中设置),最多为tickTime的20倍。ZooKeeper客户端API容许访问协商超时。
当客户机(会话)从ZK服务集群中分区时,它将开始搜索会话建立期间指定的服务器列表。最终,当客户机和至少一个服务器之间的链接被从新创建时,会话将再次过渡到“connected”状态(若是在会话超时值内从新链接),或者过渡到“expired”状态(若是在会话超时以后从新链接)。不建议建立一个用于断开链接的新会话对象(一个新的zookeeper .class或c绑定中的zookeeper句柄)。ZK客户端库将为您处理从新链接。特别是,咱们在客户端库中构建了启发式来处理诸如“羊群效应”等问题。只有当您被通知会话过时(强制)时才建立一个新会话。
会话过时由ZooKeeper集群自己管理,而不是由客户机管理。当ZK客户机与集群创建一个会话时,它提供了上面详细描述的“超时”值。集群使用此值来肯定客户机的会话什么时候过时。当集群在指定的会话超时期间(即没有心跳)内没有收到客户机的消息时,就会发生终止。在会话过时时,集群将删除该会话拥有的任何/全部临时节点,并当即通知任何/全部链接的客户机更改(任何监视这些znode的人)。此时,过时会话的客户机仍然与集群断开链接,直到/除非它可以从新创建到集群的链接,不然不会通知它会话过时。客户机将保持断开链接状态,直到使用集群从新创建TCP链接,此时过时会话的监视程序将收到“会话过时”通知。
ZooKeeper会话创建调用的另外一个参数是默认的监视程序。当客户机中发生任何状态更改时,会通知监视者。例如,若是客户端失去与服务器的链接,客户端将获得通知,或者若是客户端会话过时,等等。此监视程序应该考虑断开初始状态(即在任何状态更改事件由客户端库发送到监视程序以前)。在新链接的状况下,发送给观察者的第一个事件一般是会话链接事件。
会话经过客户机发送的请求保持活动状态。若是会话空闲一段时间会超时会话,客户端将发送一个PING请求来保持会话活动。这个PING请求不只容许ZooKeeper服务器知道客户机仍然处于活动状态,并且还容许客户机验证它到ZooKeeper服务器的链接是否仍然处于活动状态。PING的时间足够保守,能够确保有合理的时间检测死链接并从新链接到新服务器。
一旦成功创建到服务器的链接(链接)基本上有两个状况下,客户端自由生成connectionloss(结果用c代码绑定,用Java异常,请查看API文档绑定具体细节)当执行一个同步或异步操做,下列是适用的:
1.应用程序调用再也不有效的会话上的操做
2.当服务器上有挂起的操做时,ZooKeeper客户端断开与服务器的链接,有一个挂起的异步调用。
Updating the list of servers 更新服务器列表:容许客户端经过提供一个新的逗号分隔的host:port对列表来更新链接字符串,每一个host:port对对应于ZooKeeper服务器。该函数调用一个几率负载平衡算法,该算法可能致使客户端与其当前主机断开链接,目标是在新列表中实现每一个服务器预期的统一链接数。若是客户端链接到的当前主机不在新列表中,此调用将始终致使链接被删除。不然,则根据serv的数量来决定。例如,若是之前的链接字符串包含3台主机,而如今列表包含这3台主机和另外2台主机,那么链接到这3台主机的40%的客户端将移动到其中一台新主机,以平衡负载。该算法将致使客户端断开与当前主机的链接,该链接的几率为0.4,在本例中,将致使客户端链接到随机选择的两个新主机之一。另外一个例子,假设咱们有5如今主机和主机的更新列表,删除2,剩下的客户端链接到3主机将保持联系,而全部客户机链接到2删除主机须要搬到一个3主机,随机选取的。若是链接被删除,客户端将切换到一种特殊模式,在这种模式中,他将使用几率算法选择新服务器链接,而不只仅是循环。在第一个示例中,每一个客户机决定断开链接的几率为0.4,可是一旦作出了这个决定,它将尝试链接到一个随机的新服务器,只有当它不能链接到任何一个新服务器时,它才尝试链接到旧服务器。在找到一个服务器以后,或者尝试新列表中的全部服务器,可是链接失败以后,客户机返回到正常的操做模式,从connectString中选择一个任意的服务器,并尝试链接到它。若是失败,它将继续在轮询中尝试不一样的随机服务器。
ZooKeeper Watches
1.当数据发生变化时,将向客户端发送一次触发One watch事件。例如,若是客户机执行getData("/znode1", true),而后更改或删除/znode1的数据,客户机将得到/znode1的watch事件。若是/znode1再次更改,则不会发送任何watch事件,除非客户端完成了设置新Watches的另外一次读取。
2.发送到客户端意味着一个事件正在发送到客户端途中,可是在成功地将更改操做的返回代码发送到发起更改的客户端以前,可能没法到达客户端。手表以异步方式发送给观察者。ZooKeeper提供了一个订购保证:客户端在第一次看到watch事件以前,永远不会看到设置了Watches的更改。网络延迟或其余因素可能致使不一样的客户端在不一样的时间看到Watches并从更新中返回代码。关键是不一样客户看到的全部东西都有一个一致的顺序。
ZooKeeper access control using ACLs
ZooKeeper使用acl控制对其znode (ZooKeeper数据树的数据节点)的访问。ACL实现与UNIX文件访问权限很是类似:它使用权限位容许/不容许对节点和应用这些位的范围执行各类操做。与标准UNIX权限不一样,ZooKeeper节点不受user(文件全部者)、group和world(其余)三个标准范围的限制。ZooKeeper没有znode的全部者的概念。相反,ACL指定与这些id关联的id和权限集。
还要注意,ACL只属于特定的znode。它尤为不适用于儿童。例如,若是/app只能经过ip:172.16.16.1读取,而/app/status是世界可读的,那么任何人均可以读取/app/status;acl不是递归的。
ZooKeeper支持可插入的身份验证方案。id使用表单scheme:expression指定,其中scheme是id对应的身份验证方案。该方案定义了一组有效表达式。例如,ip:172.16.16.1是使用ip方案的地址为172.16.16.1的主机的id,而digest:bob:password是使用摘要方案的用户的id,其名称为bob。
当客户端链接到ZooKeeper并验证自身时,ZooKeeper将与客户端链接对应的全部id关联起来。当客户机试图访问某个节点时,将根据znodes的acl检查这些id。acl由一对(scheme:expression, perms)组成。表达式的格式是特定于方案的。例如,这对(ip:19.22.0.0/16, READ)将读权限授予ip地址以19.22开头的任何客户机。
ZooKeeper支持如下权限:
CREATE: you can create a child node
READ: you can get data from a node and list its children.
WRITE: you can set data for a node
DELETE: you can delete a child node
ADMIN: you can set permissions
3、Zookeeper集群的搭建
前面我讲解了ZK的单机搭建,如今讲解一下ZK集群的搭建,咱们以三个节点的集群为例,因为没有真实环境条件,只能在一台主机上搭建三个节点的伪集群,集群搭建过程当中要注意几个点,下面我会逐一进行说明。
用第一节讲解的环境进行下一步操做,首先再建立三个配置文件,因为路径的缘由,我对这三个配置文件分别作了符号链接,文件内容以下:
[root@8c649f93f3bc zookeeper-3.4.14]# cat zoo1.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/zoo1
clientPort=2182
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668
[root@8c649f93f3bc zookeeper-3.4.14]# cat zoo2.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/zoo2
clientPort=2183
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668
[root@8c649f93f3bc zookeeper-3.4.14]# cat zoo3.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/zoo3
clientPort=2184
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668
须要说明的是,每一个配置文件的端口号和dataDir不要重复,server.no=ip:port1:port2 ,其中no为集群的标识符,ip为主机地址,port1为集群内部通信的端口,port2为集群内部选举leader的端口。
而后在数据目录/var/lib/zookeeper/zoo3下建立myid文件,文件的内容和你的server id 保持一致,不然启动时会报错,内容以下:
[root@8c649f93f3bc zoo1]# cat myid
1
[root@8c649f93f3bc zoo2]# cat myid
2
[root@8c649f93f3bc zoo3]# cat myid
3
编辑好配置文件就能够启动服务了,以下所示:
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh start zoo1.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo1.cfg
Starting zookeeper ... STARTED
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh start zoo2.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo2.cfg
Starting zookeeper ... STARTED
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh start zoo3.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo3.cfg
Starting zookeeper ... STARTED
查看集群各个节点的状态及所处的模式,以下所示
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh status zoo1.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo1.cfg
Mode: follower
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh status zoo2.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo2.cfg
Mode: leader
[root@8c649f93f3bc zookeeper-3.4.14]# bin/zkServer.sh status zoo3.cfg
ZooKeeper JMX enabled by default
Using config: /home/data/zookeeper/zookeeper-3.4.14/bin/../conf/zoo3.cfg
Mode: follower
官方文档给的建议是主机的数量是奇数(2n+1),集群中容许坏的最多数量为n个。
以上就是对Zookeeper的简单介绍,知识点很杂,有些较为难理解,若是感兴趣的话,就去读官方的英文文档,下篇文章见,拜拜!