2018年第16周-ZooKeeper基本概念(配搭建过程和Master-Workers例子)

背景

随着计算机的硬件和操做系统二者相辅相成地发展,从早期的ENIAC计算机到如今的x86的计算机,从之前的单一控制终端(Single Operator, Single Console, SOSC)的操做系统到如今百花争鸣的操做系统(如MacOS、Windows、Linux等),现代的操做系统发展还有一个最重要的特征是网络的出现,网络促进了网络操做性系统和分布式操做系统的出现。对于网络操做系统来讲,其任务是将多个计算机虚拟成一个计算机。传统的网络操做系统是在现有操做系统的基础上增长网络功能,而分布式操做系统则是从一开始就把对多计算机的支持考虑进来,是从新设计的操做系统,因此比网络操做系统效率高。分布式操做系统除了提供传统操做系统的功能外,还提供多计算机协做的功能。 node

根据上述的发展,咱们在其基础上构建的应用也分为集中式系统和分布式系统。集中式系统具备明显的单点问题。大型主机虽然在性能和稳定性方面表现卓越,但这不表明它永远不会出问题。另外随着业务的不断发展,用户访问迅速提升,计算机系统的规模也在不断扩大,在单一大型主机上进行的系统的扩容每每比较困难。 算法

按照正常的套路来讲,下一步应该是说随着PC的性能不断提高和网络技术的快速普及,因此不少企业就开始去掉大型机,从而改用普通PC来做为分布式的计算机系统。这样说就太平淡了,企业也不是这么容易说变就变,就算谷歌开始的时候敢用可靠性较低的服务器的一个重要缘由,在于它是最初并不为用户提供存储和计算服务,服务器都是本身内部使用。若是计算到一半死机了,那就再从死机的断点从新启动计算。甚至若是一开始有些中间数据丢失了,那就再产生一遍。这时它的可靠性不像银行那样重要。2004年当谷歌开始提供Gmail服务,为了确保用户的数据不丢失,它采用了3x3九台服务器存一组数据,这个成本就不低了。同时期,雅虎等公司采用两台可靠性的服务器存储邮件。这时期,谷歌的Gmail是很是赔钱的。后来谷歌的云存储部门用软件实现了5台服务器分布式存储,达到过去九台服务器的可靠性,Gmail的成本才降下来。谷歌千方百计用廉价服务器集群取代超级计算机的过程远比不少人想象的复杂。这种想法其实早在谷歌以前就有了,可是其它公司就是由于没法解决其中的不少技术细节问题,使得采用大量低可靠性的服务器带来的好处尚未它带来的麻烦多,最终都放弃了。(这里引伸出谷歌的文化之一)谷歌把事作到了极致,克服了各类技术困难,最后作成了。数组

企业吸取已证明可行的经验是很快速,因而后来亚马逊等公司也实现了廉价服务器集群取代超级计算机的功能。其中在国内,最为典型的就是阿里巴巴的“去IOE”活动,安全

也正是在这个大背景之下,还有在大数据和云计算驱动之下,ZooKeeper在雅虎里诞生了。用于解决不少分布式系统下的问题。当设计一个使用ZooKeeper的应用时,最好就是将应用数据和协调数据给分开。如邮件系统,用户只关心他们的邮箱内容,而不须要知道哪台邮箱服务器在处理。所以在这里邮箱内容就是应用数据,而协调数据则是具体是哪台邮件服务器在处理。服务器

分布式的问题

因为分布式系统概念有不少种,在这里咱们定义为:一个系统由多个组件组成,而每一个组件独立并同时运行在不一样的物理机器上。网络

分布式系统一诞生就面临诸多的难题和挑战。典型的问题有这些:session

  • 通讯失败(Communication Failure)

因为分布系统引入了网络因素,而网络自己是不稳定的。所以分布式系统中各个结点以前进网络通讯时,会出现消息丢失和消息延迟等现象。架构

  • 网络分区(Network Partion)

网络分区,也称脑裂(split-brain),集群中部分节点之间不可达而引发的(或者由于节点请求压力较大,致使其余节点与该节点的心跳检测不可用)。当上述状况发生时,不一样分裂的小集群会自主的选择出master节点,形成本来的集群会同时存在多个master节点。并发

  • 三态

由于通讯失败的问题,会带来其中一个问题是三态,三态即成功、失败与超时。分布式系统的每一次请求与响应都会有这三种结果。最麻烦的是超时,由于它存在这两种可能:
1.因为通信失败,该请求(消息)并无被成功地发送到接收方,而是在发送过程当中就丢失了。
2.该请求(消息)成功地被接收方接收后,并进行了处理,可是在将响应反馈给发送方时,发生了消息丢失现象。框架

  • 节点故障

这也是属于通讯失败的状况,但着重点是说,机器自身挂了,没法发出消息。有多是宕机或负荷严重的状况致使的。

上述分布式问题致使了一致性问题难以解决,并且在2002年的时候,MIT的Seth Gibert和Nancy Lynch证实的CAP定理。这个定理是:系统不可能同时知足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)。
既然是逻辑证实出来的,那它就必定对的。这也是分布式系统的边界或者说是上限。因此人们开始在一致性和可用性上权衡,从而诞生不少算法如2PC、3PC和Paxos算法,这些都影响着ZooKeeper的设计。
ZooKeeper不能解决全部分布式系统的问题。但它提供了一个很好框架去处理这些问题。
ZooKeeper为分布式系统提供了协调功能和控制冲突。协调意思是说几个进程须要一块儿作一些事情,例如,在Master-Worker分布式架构里,worker通知master它可用,master所以分派任务给它。
而控制冲突则不同:它更可能是这种情形,两个进程是不能同时进行,一个必须等待一个才能进行。例如,在Master-Worker分布式架构里,咱们但愿只有一个进程变成master,因此当多个进程同时激活本身为master时,必须实现互斥( mutual exclusion),只能有一个进程得到锁,并成为master角色。

ZooKeeper的API提供了:

  • 强一致性,顺序和持久化的保证
  • 提供实现同步的能力
  • 一个更简单的方法处理分布式系统中的并发问题。

ZooKeeper的基础

咱们将围绕一个例子去讲述概念并实践。这个例子是:Master-Worker分布式架构。
架构图以下:

clipboard.png

Master进程的职责时跟踪Worker和任务,并将任务分派给Worker。为了实现这个Master-Worker系统,咱们必须解决这三个关键问题:
Master故障
若是master故障而且变得不可用,系统就不能分配新任务或从新分配失败的任务。

Woker故障
若是worker故障,则分配给它的任务则完成不了。

通讯失败
若是master和worker不能交换信息,则worker可能没法获取分派给它的任务。

为了解决上述问题,这个系统必须有如下功能:能在一个master挂掉以后,从新选择一个新的master;判断那些woker是可用的;当worker与master由于网络分区失去与master链接时可以从新分派任务;当一个节点得到锁以后发生网络分区或挂掉时,需让这个锁失效。

从新分配任务有如下状况:若是任务可重复执行,则能够无需任何校验的把这任务从新分派。但若是这个任务是不能重复执行的,则须要协调多个worker执行任务的状况。

znode

ZooKeeper并无直接提供上述的功能,而是提供一个跟文件系统很像的API。这个被组织称树结构,每一个结点都存储很小的数据的结点(不超过1M),被称为znode。以下图,根结点包含4个结点,其中3个又是分支结点,拥有叶子节点。而叶子结点就包含数据。

clipboard.png

不存在的结点在znode里也是蕴含信息。如在这个Master-Worker例子里,若是master结点不存在,则代表master还没选出来。另外根据上图,咱们能够看出/workers结点是当前系统中全部可用的woker的父节点。foo.com:2181是worker的信息。若是woker不可用了,就应该把对于的结点从/worers上删除。
而/tasks结点是父结点,其子结点是已经被建立的任务,且等待被执行。Master-Worker例子里,客户端就能够在/tasks下建立一个结点来表明一个新任务,而且等待该任务的状态。
最后/assign结点的子结点是全部已经被分派给worker的任务。

znode能够有和能够没有数据。若是有数据,数据类型必须是字节数据(Byte Array)。字节数组的含义解释就须要各自应用决定,ZooKeeper没有提供解析这字节数组的功能。

ZooKeeper的命令行提供了一下API:

create /path data

建立一个znode结点名为/path,包含数据data

delete /path

删除znode结点/path

exists /path

检查是否有/path结点

setData /path data

设置/path结点的数据

getData /path

获取/path结点数据

getChildren /path

获取/path结点的子结点列表

znode的模式

在建立znode结点时,咱们能够指定模式(mode),不一样模式决定znode结点的不一样行为:

永久(Persistent)和临时(Ephemeral)znode结点

znode结点只能是永久结点或者是临时结点。永久结点/path只能被delete命令删除。而临时结点,在建立该结点的客户端故障了或失去与ZooKeeper失去链接时,这个结点会被删除。
在Master-Worker例子里,咱们须要维护任务的分派状况,哪怕master故障了。
临时znode结点传递着这样的信息:结点的建立者的session有效,则结点的应用才能存在。例如,master的结点在Master-Worker例子里就是临时的。master故障时,master结点也不该该存在。一样的也适合worker的状况。
由于临时znode结点在它的建立者的session超时失效时被删除,则咱们不容许临时结点拥有子结点。

序列znode结点(Sequantial Znodes)

一个znode能够被设置为序列(sequential)。一个序列结点时惟一的,单调递增的整数。是在path后面追加序列数据。例如,若是一个客户端建立一个序列znode结点/task/task-,ZooKeeper会分配一个序列,如1,追加到路径上,则为/task/task-1。序列znode结点提供一个简便地方法去建立拥有惟一名字的znode结点。也能够被用来查看建立znode结点的顺序。

总结

所以总的来讲,znode有如下四个模式:persistent, ephemeral, persistent_sequential和ephemeral_sequential。

Watch与通知(Watches and Notifications)

因为是远程访问ZooKeeper,因此访问znode结点是很是昂贵的:高延迟或多无用的操做。考虑如下状况,若是下图,第二次使用getChildren /task返回的是一样的值,所以时没有必要的。

clipboard.png

这是轮训(polling)的广泛出现的问题。咱们使用一种叫通知(notifications)的机制来代理客户端的轮训:客户端在ZooKeeper上注册接收znode结点变化的通知。指定一个znode结点,接受其一个通知的注册,这过程叫 设置watch。一个watch是单步操做(one-shot operation),也就是说一个watch仅仅触发一个通知。若是想接受多个通知,则须要在接受到通知后,从新设置watch。设置watch和接受通知的过程以下图:

clipboard.png

版本(Version)

每个znode结点都会有一个版本号,这个版本号在每次结点的数据发生改变都会递增。这样ZooKeeper的一些API操做就能够带上条件,这操做如setData和Delete。设置数据时能够带上版本号,版本号匹配不上则操做失败。操做过程以下图:

clipboard.png

ZooKeeper的架构

clipboard.png

客户端能够经过client库来与ZooKeeper节点进行通讯。
ZooKeeper服务能够有两种模式:单机(standalone)和法定人数(quorum)。单机模式也就是单个服务器,ZooKeeper的状态不会被复制(replicate)。而在法定人数模式下,则会有一组ZooKeeper服务器,也称为ZooKeeper套装(ZooKeeper ensemble),节点之间会复制ZooKeeper的状态,并一块儿提供服务。

ZooKeeper的法定人数

在法定人数模式下,ZooKeeper会冗余(replicate)它的数据到各个服务器里。当若是客户端必须等待每一个服务器都保持好它的数据,才能往下进行操做,则延迟将会难以接受。在现实世界的公共事务管理里,法定人数是要求出席投票的最低人数。而在ZooKeeper里,法定人数是保证ZooKeeper可以正常工做,可用,的最少服务器数量。这个最少数量也是保证至少有这么多台服务器是同步了数据的。这样才能保证数据是被安全保存。如咱们用5台ZooKeeper服务器,则法定人数则为3台。只要有3台服务器保存了数据,客户端则能够往下继续本身的事情,另外两天服务器最终会同步刚刚保存的数据。

会话(Session)

客户端在对ZooKeeper发送任何请求以前,都得先与ZooKeeper创建会话(session)。会话这个概念在ZooKeeper里是很是重要且关键的。全部操做都必须关联着会话。当一个会话由于某些缘由结束时,经过这会话建立的临时结点也将会伴随这会话的结束而被删除。
客户端是经过TCP链接来与服务器通讯,客户端只能链接一个服务器。若是客户端链接的一台服务器挂了,则session将会移动到下一台服务器,这个移动过程是透明的,由ZooKeeper负责处理的。
会话提供了有序保证(order guarantees),意思是一个会话里的操做都是FIFO的顺序。但跨session操做时,哪怕session不是重叠,而是连续的不一样的session,这个FIFO的顺序也会被破坏,如:

  • 客户端创建一个session,并异步的连续的发送两个操做,create /tasks和/workers.
  • 第一个session失效
  • 客户端创建其余session,也发送一个异步请求,create /assign

在这种状况下,是有可能仅仅只有/tasks和/assign被建立,/workers而没有被建立。这是由于被交错的会话给打断了。

ZooKeeper的命令行体验

经过命令行来操做ZooKeeper来实现Master-Worker这个例子。

安装ZooKeeper(quorum模式)

1.下载安装包zookeeper-3.4.5.tar.gz

2.解压zookeeper-3.4.5.tar.gz到/usr/local

tar -zxvf zookeeper-3.4.5.tar.gz

3.建立zoo.cfg配置文件

mv conf/zoo_sample.cfg conf/zoo.cfg

4.修改zoo.cfg配置文件,修改以下

#防止把根分区给被占满
dataDir=/usr/local/zookeeper-3.4.5/tmp  
clientPort=2181
server.1=zk1.jevoncode.com:2888:3888
server.2=zk2.jevoncode.com:2888:3888
server.3=zk3.jevoncode.com:2888:3888

其中2181是ZooKeeper服务器的开放给客户端访问的端口,而2888和3888是ZooKeeper节点之间用于通讯和leader选举。
5.在各个结点的/usr/local/zookeeper-3.4.5/tmp目录下创建myid文件,内容为server的id,如给zk1.jevoncode.com这个结点添加myid

echo "1">/usr/local/zookeeper-3.4.5/tmp/myid

6.在每一个结点启动ZooKeeper服务

./bin/zkServer.sh start

7.查看每一个结点的状态,就能够看到leader和follower了

./bin/zkServer.sh status

实现Master-Worker这个例子

咱们经过zkCli.sh工具来实现Master-Worker这个例子的功能。
Master-Worker这个例子涉及到三个角色:
Master

这个master负责监控新woker和任务,并分派任务给可用的worker。

Worker

worker将本身注册到该系统中,并保证master可以看到它是可用的,可执行任务,而后监听新任务。
客户端
客户端建立新任务并等待系统的响应

Master角色

1.在窗口A链接ZooKeeper

./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181

2.在窗口A建立临时znode结点/master

create -e /master "master1.jevoncode.com:2223"

3.在窗口B链接ZooKeeper

./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181

4.在窗口B建立临时znode结点/master,模拟两个进程激活master角色。这个会失败

#会返回Node already exists: /master
create -e /master "master2.jevoncode.com:2223"

5.在窗口B,既然/master已存在,则需监听/master,以便当窗口A的master故障时,能快速恢复过来。stat命令是获取znode属性,而后后续参数true是设置一个watch在/master上

stat /master true

6.当窗口A退出时,窗口B会收到一下通知:

WatchedEvent state:SyncConnected type:NodeDeleted path:/master

7.此时窗口B就能够激活本身称为master

create -e /master "master2.jevoncode.com:2223"

Worker角色、任务和任务分派

在继续客户端和Worker操做以前,咱们须要建立三个父节点,/workers, /tasks和/assign

create /workers ""
create /tasks ""
create /assign ""
ls /

另外Master角色需监听worker和任务

ls /workers true
ls /tasks true

Worker角色

1.在窗口C链接ZooKeeper

./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181

2.建立一个worker结点

create -e /workers/worker1.jevoncode.com "worker1.jevoncode.com:2224"

3.此时Master角色的窗口会受到通知

WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/workers

4.worker需建立一个父节点来接受任务分派,并watch

create /assign/worker1.jevoncode.com ""
ls /assign/worker1.jevoncode.com true

客户端角色

1.在窗口D链接ZooKeeper

./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181

2.提交任务,在这里是建立一个序列znode结点。

create -s /tasks/task- "cmd"

3.设置一个watch,等待任务完成

ls /task/task-0000000000 true

4.当任务建立是,Master的窗口将会收到通知

WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks

5.此时Master窗口查看任务,查看可用worker并分派任务

ls /tasks
ls /workers
create /assign/worker1.jevoncode.com/task-0000000000 ""

6.此时Worker窗口C收到通知

WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged
path:/assign/worker1.example.com

7.Worker窗口C,查看任务,并执行完任务后修改任务状态

ls /assign/worker1.jevoncode.com
create /tasks/task-0000000000/status "done"

8.客户端收到通知,并查看任务完成结果

#通知
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged
path:/tasks/task-0000000000

#查看结果
get /tasks/task-0000000000
相关文章
相关标签/搜索