了解zookeeper-续

来源:互联网 发布:授权平台源码 编辑:程序博客网 时间:2024/05/15 14:29

4、共享锁(Locks)

共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
共享锁机制图

5、队列管理

Zookeeper 可以处理两种类型的队列:
1) 当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
2) 队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。
同步队列用 Zookeeper 实现的实现思路如下:
创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 / synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start。
用流程图更容易理解:
队列管理机制
FIFO 队列用 Zookeeper 实现思路如下:
实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。
利用Zookeeper实现带优先级的队列,只需要简单地修改普通队列中节点的命名方式:以“queue-YY”来做队列元素的前缀,其中YY为节点的优先级,依据Linux的方式,数值越小,优先级越高。并且,处理队列的客户端,在处理完一个节点之后,需要调用sync()以保证有优先级高的节点插入时,能够先获得处理资源。

5、障碍墙(Barriers)

分布式系统利用障碍墙来保证对于一组数据节点的处理被某个条件阻塞。直到条件被满足的时候,所有数据节点同时开始处理。在Zookeeper中,可以利用一个“障碍墙节点”(算法中使用b表示该节点)来实现这个功能:
1、客户端对于该“障碍墙”节点调用exists(b, true)函数,设置watch
2、如果exists()返回false,“障碍墙”节点不存在,客户端继续执行
3、如果exists()返回true,客户端等待“障碍墙”节点的watch事件
4、当watch事件被激发时,跳回1执行

7、双重障碍墙(Double Barriers)

双重障碍墙用于保证客户端同步开始和结束某个计算过程。当足够的客户端被障碍墙阻挡的时候,计算开始执行。当所有计算都结束的时候,所有客户端一起脱离障 碍墙。利用Zookeeper实现双重障碍墙的同步机制,在计算启动之前,客户端通过在“障碍墙节点”(算法中使用b表示该节点)注册来加入同步过程。而 在计算结束时,客户端从“障碍墙节点”注销。
在以下算法代码中,n是客户端的Zookeeper名字标识,p标识了一个客户端,pmax标识了编号最大的一个客户端,pmin标识了编号最小的一个客户端。
这里写图片描述
如算法所示,在进入“障碍墙”的过程中,客户端在“障碍墙”节点下创建代表自己的临时节点。当最后一个客户端进入后,它能检测到“障碍墙”节点已经有x个子节点,此时,该客户端创建“ready”节点,通知等待“监视”事件的所有客户端同时开始计算过程。
这里写图片描述
在计算结束,所有客户端需要删除各自子节点,并同时离开“障碍墙节点”。注意,在上述退出协议中,最后一个被删除的子节点是序号最小的子节点,该子节点对 应的客户端将“监视点”设置在当前序号最大子节点上。当该序号最大的子节点被删除后,继续选择当时序号最大的子节点等待。所有其他客户端都等待在序号最小 的子节点上,当该节点被删除之后,所有其他的客户端同时离开。

8、互斥锁

分布式互斥锁的定义是在任意时刻,分布式系统中的两个客户端不会同时认为自身持有同一个互斥锁。同样,首先我们需要定义一个锁节点,想要获得锁的客户端,按照以下过程操作:
这里写图片描述
需要注意的几点:
1) 每次删除锁节点的子节点时,只会唤醒一个客户端。
2) 在这个实现方案中不需要轮询和超时处理

9、读写锁

利用Zookeeper实现的读写锁,是在互斥锁的基础上实现的。
这里写图片描述
这里写图片描述
注意,此处实现之中有多个”read-”子节点同时等待在”write-”子节点上,但这样的设计不会有副作用。这是因为,”read-”子节点在被唤醒的时候,都能获得处理资源,而不是只有某个或者某些客户端能继续执行。

10、可恢复(recoverable)的读写锁

通过对读写锁做不大的修改,便可实现简单的可恢复读写锁(用于防止某些锁被客户端长期持有)。方法如下:
无论是在申请读锁还是申请写锁时,在调用create()之后,立即对于该节点调用getData()并设置“监视点”(注意,如果getData()接 收到了create事件,则重新getData(),并设置“监视点”)。getData()用于监视该节点数据中是否出现“unlock”字符串。需要 锁的其他客户端通过向该节点数据中写入”unlock”来提醒持有锁的客户端释放锁。
注意在这个可恢复读写锁的实现机制中,有一个关键点,即,持有锁的“客户端”必须同意释放锁。在很多情况下,持有锁的客户端需要保留锁来进行未完成的操作。

11、二阶段提交(Two-phased Commit)

二阶段提交机制用于保证分布式系统中的客户端都同意提交或者放弃某事务。
在Zookeeper中,可以在有协调客户端(coordinator)的基础上,实现二阶段提交机制。首先协调客户端创建一个事务节点(如:/app /Tx),为每个参与客户端建立一个子节点(如:/app/Tx/s_i),这些子节点的数据为空。当各个参与客户端接收到协调客户端发送的事务时,它们 别的客户端对应的节点设置“监视”。并通过写自己对应的节点的数据来告诉别的客户端自己是否确认提交事务。需要注意的是,由于很多情况下,只要有一个客户 端不能确认提交,事务就会被丢弃,所以整个处理时间可能很短。

以上总结的内容来源于网络…

0 0