整套大数据学习资料(视频+笔记)百度网盘无门槛下载:http://www.edu360.cn/news/content?id=3377

14.4.3锁服务

hadoop 花牛 12℃ 0评论

分布式锁能够在一组进程之间提供互斥机制,使得在任何时刻只有一个进程可以持有锁。分布式锁可以用于在大型分布式系统中实现领导者选举,在任何时间点,持有锁的那个进程就是系统的领导者。

不要将ZooKeeper自己的领导者选举和使用ZooKeeper基本操作实 现的般的领导者选举服务混为一谈(事实上,ZooKeeper中包含有 一个领导者选举服务的实现。ZooKeeper自己的领导者选举机制是不对外公开的,我们这里所描述的一般领导者选举服务则不同,它是为那些需要所有进程与主进程保持一致的分布式系统所设计的。

为了使用ZooKeeper来实现分布式锁服务,我们使用顺序znode来为那些 竞争锁的进程强制排序。思路很简单:首先指定一个作为锁的znode,通常 用它来描述被锁定的实体,称为然后希望获得锁的客户端创建一些短暂顺序znode,作为锁znode的子节点。在任何时间点,顺序号最小的 客户端将持有锁。例如,有两个客户端差不多同时创建znode,分别为/leaer/lock-1和/leader/lock-2的客户端会持有锁,因为它的znode顺序号最小。ZooKeeper服务是顺序的仲裁者,因为它负责分配顺序号。

通过删除znode/leader/lock-1即可简单地将锁释放,另外,如果客户端进程死亡,对应的短暂znode也会被删除。接下来,创建znode/leader/locker-2的客户端将持有锁,因为它的顺序号紧跟前一个。通过创建一个关于znode删 除的观察,可以使客户端在获得锁时得到通知。

如下是申请获取锁的伪代码。

(1) 在锁znode下创建一个名为lock-的短暂顺序znode,并且记住它的 实际路径名(create操作的返回值)。

(2) 査询锁znode的子节点并且设置一个观察。

(3) 如果步骤1中所创建的znode在步骤2返回的所有子节点中具有最小的顺序号,则获取到锁,退出。

(4) 等待步骤2中所设观察的通知,转到步骤2。

1.羊群效应 

虽然这个算法是正确的,但还是存在一些问题。第一个问题是这种实现会受到“羊群效应”(herd effect)的影响。在有成百上千客户端的情况,所有的客户端都在尝试获得锁,所以每个客户端都会在锁znode上设置一个观 察,用于捕捉子节点的变化。每次锁被释放或一个新进程开始申请锁的时候,观察都会被触发并且每个客户端都会收到一个通知。“羊群效应”就 是指这种大量客户端收到同一事件的通知,但实际上只有很少一部分需要 处理这一事件。在这种情况下,只有一个客户端会成功地获取锁,但是维护的过程以及向所有客户端发送观察事件会产生峰值流量,这会对ZooKeeper服务器造成压力。

为了避免出现羊群效应,我们需要优化发送通知的条件。关键在于仅当前 一个顺序号的子节点消失时才需要通知下一个客户端,而不是删除(或创建) 任何子节点时都进行通知。在我们的例子中,如果客户端创建了znode /leader/lock-1/leader/lock-2 /leader/lock-3,那么有当/leader/lock-2失时才需要通知/leader/lock-3对应的客户端;/leader/lock-1/消失或有新的znode//leader/lock-4加入时,不需要通知该客户端。

1. 可恢复的异常

这个申请锁的算法目前还存在另一个问题,就是不能处理因连接丢失而导致的create操作失畋。如前所述,在这种情况下我们不知道操作是成功还是 失败。由于创建一个顺序znode是非幂等操作,所以我们不能简单地进行重试。原因在于如果第一次创建已经成功,重试会使我们多出一个永远删不掉的孤儿znode(至少到客户端会话结束前)。最不幸的结果是还将会出现死锁。 问题在于,在重新连接之后客户端不能够判断它是否已经创建过子节点。 解决方案是在znode的名称中嵌入一个ID,如果客户端出现连接丢失的情况,重新连接之后它便可以对锁节点的所有子节点进行检查,看看是否有子节点的名称中包含其ID。如果有一个子节点的名称包含其ID,它便知道自己的创建操作已经成功,不需要再创建子节点。如果没有子节点的名称中包含其ID,则客户端可以安全地创建一个新的顺序子节点。

客户端会话的ID是一个长整数,并且在ZooKeeper服务中是唯一的,因此非常适合在连接丢失后用于重新识别客户端。可以通过调用Java ZooKeeper 类的getSessionldO方法来获得会话的ID

在创建短暂顺序znode时应当采用这样的命名方式, ZooKeeper在其尾部添加顺序号之后,znode的名称会形如lock-<sessionID><sequenceNumber> 。由于顺序号对于父节点来说是唯一的,但对于子节点名 并不唯一,因此采用这样的命名方式可以让子节点在保持创建顺序的同时 能够确定自己的创建者。

3. 不可恢复的异常

如果一个客户端的ZooKeeper会话过期,那么它所创建的短暂znode将会 被删除,已持有的锁会被释放,或者是放弃了申请锁的位置。使用锁的应用程序应当意识到它已经不再持有锁,应当清理它的状态,然后通过创建 并尝试申请一个新的锁对象来重新启动。注意,这个过程是由应用程序控制的,而不是锁,因为锁是不能预知应用程序需要如何清理自己的状态。

4. 实现

正确地实现一个分布式锁是一件棘手的事,因为很难对所有类型的故障都进行正确的解释处理。ZooKeeper带有一个Java语言编写的生产级別的锁实现,名为WriteLock,客户端可以很方便地使用它。

转载请注明:全栈大数据 » 14.4.3锁服务

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址