ZooKeeper分布式过程协同技术详解 2章

2.1 ZooKeeper基础
znode节点:ZooKeeper操作和维护⼀个⼩型的数据节点,采⽤类似于⽂件系统的层级树状结构进⾏管理。
主节点的znode没有数据,表⽰当前还没有选举出主节点。
在这里插入图片描述
·/workers节点作为⽗节点,其下每个znode⼦节点保存了系统中⼀个可用从节点信息。如图2-1所示,有⼀个从节点(foot.com:2181);

·/tasks节点作为⽗节点,其下每个znode⼦节点保存了所有已经创建并等待从节点执⾏的任务的信息,主-从模式的应用的客户端在/tasks下添加⼀个znode⼦节点,用来表示⼀个新任务,并等待任务状态的znode节点;

·/assign节点作为⽗节点,其下每个znode⼦节点保存了分配到某个从节点的⼀个任务信息,当主节点为某个从节点分配了⼀个任务,就会在/assign下增加⼀个⼦节点;

2.1.1 API概述
znode节点可能含有数据,也可能没有。如果⼀个znode节点包含任何数据,那么数据存储为字节数组(byte array)。字节数组的具体格式特定于每个应⽤的实现,ZooKeeper并不直接提供解析的⽀持。

create/path data :创建⼀个名为/path的znode节点,并包含数据data。
delete/path :删除名为/path的znode。
exists/path :检查是否存在名为/path的节点。
setData/path data :设置名为/path的znode的数据为data。
getData/path :返回名为/path节点的数据信息。
getChildren/path :返回所有/path节点的所有⼦节点列表。

2.1.2 znode的不同类型
持久(persistent)节点:持久的znode,如/path,只能通过调⽤delete来进⾏删除。
临时(ephemeral)节点:当创建该节点的客户端崩溃或关闭了与ZooKeeper的连接时,这个节点就会被删除。

有序节点:⼀个有序znode节点被分配唯⼀个单调递增的整数。当创建有序节点时,⼀个序号会被追加到路径之后。例如,如果⼀个客户端创建了⼀个有序znode节点,其路径为/tasks/task-,那么ZooKeeper将会分配⼀个序号,如1,并将这个数字追加到路径之后,最后该znode节点为/tasks/task-1。

2.1.3 监视与通知
监视点(watch):通过对znode设置监视点(watch)来接收通知。监视点是⼀个单次触发的操作,意即监视点会触发⼀个通知。为了接收多个通知,客户端必须在每次通知后设置⼀个新的监视点。
在这里插入图片描述2.1.4 版本
版本号:每⼀个znode都有⼀个版本号,它随着每次数据变化⽽⾃增。两个API操作可以有条件地执⾏:setData和delete。这两个调⽤以版本号作为转⼊参数,只有当转⼊参数的版本号与服务器上的版本号⼀致时调⽤才会成功。

2.2 ZooKeeper架构
独⽴模式(standalone):有⼀个单独的服务器,ZooKeeper状态⽆法复制
仲裁模式(quorum):在仲裁模式下,具有⼀组ZooKeeper服务器,我们称为ZooKeeper集合(ZooKeeper ensemble),它们之前可以进⾏状态的复制,并同时为服务于客户端的请求。

2.2.1 ZooKeeper仲裁
在仲裁模式下,ZooKeeper复制集群中的所有服务器的数据树。但如果让⼀个客户端等待每个服务器完成数据保存后再继续,延迟问题将⽆法接受,这里采用法定人数解决;

法定⼈数:则是指为了使ZooKeeper⼯作必须有效运⾏的服务器的最⼩数量。例如,我们⼀共有5个ZooKeeper服务器,但法定⼈数为3个,这样,只要任何3个服务器保存了数据,客户端就可以继续。

2.2.2 会话
会话提供了顺序保障,这就意味着同⼀个会话中的请求会以FIFO(先进先出)顺序执⾏。

2.3 开始使⽤ZooKeeper
重命名配置⽂件:mv conf/zoo_sample.cfg conf/zoo.cfg
启动服务器:bin/zkServer.sh start
前台中运⾏:bin/zkServer.sh start-foreground

启动客户端:bin/zkCli.sh

列出根(root)下的所有znode:ls /

创建⼀个名为/workers的znode:create /workers “”

删除znode:delete /workers

关闭服务器:bin/zkServer.sh stop

2.3.2 会话的状态和声明周期
在这里插入图片描述在仲裁模式中,应用需要传递可用的服务器列表给客户端,告知客户端可以连接的服务器信息并选择⼀个进⾏连接。
客户端不能连接到这样的服务器:它未发现更新⽽客户端却已经发现的更新。ZooKeeper通过在服务中排序更新操作来决定状态是否最新。ZooKeeper确保每⼀个变化相对于所有其他已执⾏的更新是完全有序的。因此,如果⼀个客户端在位置i观察到⼀个更新,它就不能连接到只观察到i’<i的服务器上

2.3.3 ZooKeeper与仲裁模式
ZooKeeper集合多服务器配置:
initLimit=10
syncLimit=5
dataDir=./data
clientPort=2181
server.1=127.0.0.1:2222:2223
server.2=127.0.0.1:3333:3334
server.3=127.0.0.1:4444:4445

每个server.n项通过冒号分隔为三部分,第⼀部分为服务器n的IP地址或主机名(hostname),第⼆部分和第三部分为TCP端⼜号,分别⽤于仲裁通信和群⾸选举

使⽤zkCli.sh来访问集群:bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

2.3.4 实现⼀个原语:通过ZooKeeper实现锁
通过ZooKeeper来实现锁也有多种⽅式。这⾥讨论⼀个简单的⽅式来说明应⽤中如何使⽤ZooKeeper。
假设有⼀个应⽤由n个进程组成,这些进程尝试获取⼀个锁。再次强调,ZooKeeper并未直接暴露原语,因此我们使⽤ZooKeeper的接⼜来管理znode,以此来实现锁。为了获得⼀个锁,每个进程p尝试创建znode,名为/lock如果进程p成功创建了znode,就表⽰它获得了锁并可以继续执⾏其临界区域的代码。不过⼀个潜在的问题是进程p可能崩溃,导致这个锁永远⽆法释放。在这种情况下,没有任何其他进程可以再次获得这个锁,整个系统可能因死锁⽽失灵。为了避免这种情况,我们不得不在创建这个节点时指定/lock为临时节点。

2.4 ⼀个主-从模式例⼦的实现
主节点:主节点负责监视新的从节点和任务,分配任务给可⽤的从节点。
备份主节点:⼀个活动的主节点可能会崩溃,备份主节点需要接替活动主节点的⾓⾊。
从节点:从节点会通过系统注册⾃⼰,以确保主节点看到它们可以执⾏任务,然后开始监视新任务。
客户端:客户端创建新任务并等待系统的响应。

创建主节点:create -e /master “master1.example.com:2223”

备份主节点,需要在/master节点上设置⼀个监视点:stat /master true
stat命令可以得到⼀个znode节点的属性,并允许我们在已经存在的znode节点上设置监视点。通过在路径后⾯设置参数true来添加监视点。

当活动的主节点崩溃时,/master节点已经不存在了。现在备份主节点通过再次创建/master节点来成为活动主节点:create -e /master “master2.example.com:2223”

2.4.2 从节点、任务和分配
让我们先创建三个重要的⽗znode,/workers、/tasks和/assign:

[zk: localhost:2181(CONNECTED) 0] create /workers ""
Created /workers
[zk: localhost:2181(CONNECTED) 1] create /tasks ""
Created /tasks
[zk: localhost:2181(CONNECTED) 2] create /assign ""
Created /assign

在真实的应⽤中,这些znode可能由主进程在分配任务前创建,也可能由⼀个引导程序创建,不管这些节点是如何创建的,⼀旦这些节点存在了,主节点就需要监视/workers和/tasks的⼦节点的变化情况:

[zk: localhost:2181(CONNECTED) 4] ls /workers true
[]
[zk: localhost:2181(CONNECTED) 5] ls /tasks true
[]
[zk: localhost:2181(CONNECTED) 6]

通过true这个参数,可以设置对应znode的⼦节点变化的监视点

2.4.3 从节点⾓⾊
从节点⾸先要通知主节点,告知从节点可以执⾏任务。从节点通过在/workers⼦节点下创建临时性的znode来进⾏通知,并在⼦节点中使⽤主机名来标识⾃⼰:

[zk: localhost:2181(CONNECTED) 0] create -e /workers/worker1.example.com
"worker1.example.com:2224"
Created /workers/worker1.example.com
[zk: localhost:2181(CONNECTED) 1]

下⼀步,从节点需要创建⼀个⽗znode/assing/worker1.example.com来接收任务分配,并通过第⼆个参数为true的ls命令来监视这个节点的变化,以便等待新的任务:

[zk: localhost:2181(CONNECTED) 1] create /assign/worker1.example.com ""
Created /assign/worker1.example.com
[zk: localhost:2181(CONNECTED) 2] ls /assign/worker1.example.com true

2.4.4 客户端⾓⾊
客户端向系统中添加任务:

[zk: localhost:2181(CONNECTED) 0] create -s /tasks/task- "cmd"
Created /tasks/task-0000000000

客户端通过查看任务状态的znode是否创建来确定任务是否执⾏完毕,因此客户端需要监视状态znode的创建事件:

[zk: localhost:2181(CONNECTED) 1] ls /tasks/task-0000000000 true

主节点之后会检查这个新的任务,获取可⽤的从节点列表,之后分配这个任务给worker1.example.com:

[zk: 6] ls /tasks
[task-0000000000]
[zk: 7] ls /workers
[worker1.example.com]
[zk: 8] create /assign/worker1.example.com/task-0000000000 ""
Created /assign/worker1.example.com/task-0000000000
[zk: 9]

从节点接收到新任务分配的通知:

[zk: localhost:2181(CONNECTED) 3]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged
path:/assign/worker1.example.com

从节点之后便开始检查新任务,并确认该任务是否分配给⾃⼰:

WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged
path:/assign/worker1.example.com
[zk: localhost:2181(CONNECTED) 3] ls /assign/worker1.example.com
[task-0000000000]
[zk: localhost:2181(CONNECTED) 4]

⼀旦从节点完成任务的执⾏,它就会在/tasks中添加⼀个状态znode:

[zk: localhost:2181(CONNECTED) 4] create /tasks/task-0000000000/status "done"
Created /tasks/task-0000000000/status
[zk: localhost:2181(CONNECTED) 5]

之后,客户端接收到通知,并检查执⾏结果:

[zk: localhost:2181(CONNECTED) 3] get /tasks/task-0000000000/status
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值