云计算设计模式(十三)——领导人选举模式

云计算设计模式(十三)——领导人选举模式


通过协调合作,在分布式应用程序的任务实例集合执行的操作,选举一个实例作为承担管理的其他实例责任的领导者。这个模式可以有助于确保任务实例不互相冲突,导致争用共享资源,或与其他的任务实例正在执行的工作无意中干扰。

背景和问题


一个典型的云应用包括行动协调的方式很多任务。这些任务都可以是实例运行相同的代码和需要访问相同的资源,或者它们可能是可并行工作,以执行复杂计算的各个部分。

任务实例可能为多的时间自主运行,但它也可能是必要的,以协调各实例的操作,以确保它们不发生冲突,导致争用共享资源,或无意中妨碍工作,其他的任务实例正在执行。例如:
•在基于云的系统,该系统实现了横向扩展,同一个任务的多个实例可以与每个实例服务于不同的用户同时运行。如果这些实例写入到共享资源,也可能是必要的,以协调它们的操作,以防止每个实例从盲目地覆盖由他人进行的更改。
•如果任务正在执行复杂的计算以并行的单个元素,其结果将需要被聚合时,他们都完成了。

由于任务实例都是同行,没有天生的领导者,能够充当协调员或聚合器。

解决方案


单个任务实例应选充当领导者,这种情况下应协调其他从属任务实例的操作。如果所有的任务实例都运行相同的代码,他们可能都能够充当领导者。因此,选举过程必须谨慎管理,以防止两个或多个实例接管领导者的角色在同一时间。

该系统必须选择的领导者提供了一个坚固的机构。这种机制必须能够应付诸如网络中断或进程故障事件。在许多解决方案中,下属的工作情况监控的领导者,通过某种类型的心跳机制,或通过轮询。如果指定的领导者意外终止或网络故障使得领导者不通下属的工作情况,有必要为他们选出了新的领导人。

有可用于选举的领导者之间的一组任务在分布式环境中,包括几个策略:
•与排名最低的实例或进程ID选择任务实例。
竞速获得一个共享的分布式互斥。第一个任务实例获取该互斥锁处于领先地位。然而,系统必须保证,如果领导者终止或变得与系统的其余部分断开,该互斥被释放,以允许另一个任务实例成为领导者。
•实施的共同领导人选举算法,如恶霸算法或环算法之一。这些算法是相对简单的,但也有一些更复杂的技术提供。这些算法假定每个候选参选具有唯一的ID,并且,它们可以用可靠的方式在其他候选人进行通信。

 

问题和注意事项


在决定如何实现这个模式时,请考虑以下几点:
•选出一个领导者的过程应该是弹性的瞬时和永久失效。
•必须能够检测到当领导失败,或变成不可用(可能是由于通讯故障)。在这是需要这种检测的速度将取决于系统。有些系统可能能够发挥作用了一会儿没有一个领导者,在此期间造成的领导人变得不可用瞬时故障可能已被排除。在其他情况下,可能有必要立即检测领袖失败并引发新的选举。
•在实现自动缩放水平的系统中,如果系统鳞背面和关闭一些计算资源的领导者可能被终止。
•使用一个共享的分布式互斥引入外部服务,提供了互斥锁的可用性依赖。该服务可以构成一个单点故障。如果此服务应该以任何理由变得不可用时,系统将无法选出一个领导者。
•使用一个专用进程的领导者是一个比较简单的方法。然而,如果该过程失败,可能有显著延迟而被重新启动,并且将得到的延迟可能影响其他进程的性能和响应时间,如果他们正在等待领导人来协调的动作。
•实施的领导人选举算法之一手动为调整和优化代码的最大灵活性。


何时使用这个模式


使用此模式时,在分布式应用程序的任务,比如云托管解决方案,需要认真协调,也没有天生的领导者。

注意:

避免使领导者在系统中的瓶颈。领导者的目的是协调的附属任务完成的工作,它不一定有机会参加这项工作本身,尽管它应该是有能力这样做,如果任务没有当选领导人。

这种模式可能不适合:
•如果有一个天生的领导者或专用的过程,可以随时充当领导者。例如,有可能实现一个单进程,其协调该任务的实例。如果这个过程失败或变得不健康,系统可以将其关闭并重新启动它。
•如果任务之间的协调,可以很容易地通过使用更轻便的机构来实现的。例如,如果几个任务实例仅仅需要对共享资源的访问协调,一个最好的解决办法可能是使用乐观或悲观锁来控制访问该资源。
•如果一个第三方的解决方案是比较合适的。例如,微软的Azure HDInsight服务(基于Apache Hadoop的)使用所提供的ApacheZookeeper协调的map / reduce的汇总任务和汇总数据的服务。它也可以安装并在Azure的虚拟机配置动物园管理员,并将其集成到自己的解决方案,或使用可从微软开放技术的动物园管理员预置的虚拟机映像。欲了解更多信息,请参阅Apache的动物园管理员在微软的Azure在微软开放技术的网站。

例子


在列入可用于本指南中的示例代码中LeaderElection解决方案DistributedMutex项目展示了如何使用租赁在Azure存储BLOB提供了一种机制,实现共享的分布式互斥。此互斥锁可以用来选择在Azure云服务的领导者之间的一组角色的实例。第一个角色实例获得租约当选的领导人,并保持领先直至其租赁或直到它无法续租。其他角色实例可以继续监视在领导不再可用的情况下将BLOB租赁。

注意:

一个BLOB租赁是在一个blob的排他写锁。单个BLOB可以是一整租的在任何一个时间点的问题。角色实例可以请求租约在指定的斑点,而且将被授予租约,如果没有其他租赁在同一个斑点,是由这个或任何其他作用,比如举行,否则将抛出一个异常。
为了减少一个故障角色实例保留无限期租用的可能性,指定了一辈子的租约。当此期满后,租赁变为可用。然而,当一个角色实例持有的租赁也可以请求租约到期,并且将被授予租约的时间再延长。角色实例可以不断重复这一过程,如果它希望保留租约。
有关如何租用一个blob的详细信息,请参阅租赁斑点(REST API)在MSDN上。



在该实例中BlobDistributedMutex类包含RunTaskWhenMutexAquired方法,使一个角色实例试图获得租约在指定的斑点。团块(名称,容器和储存的帐户)的细节传递给构造在一个BlobSettings对象被创建的BlobDistributedMutex对象时(这个对象是包含在示例代码的简单结构)。构造函数也接受一个任务,它引用的角色实例应该运行,如果它成功收购租赁在BLOB和当选领导人的代码。需要注意的是处理获得租约的低层次细节中的代码名为BlobLeaseManager一个单独的辅助类实现。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. public class BlobDistributedMutex  
  2. {  
  3.   ...  
  4.   private readonly BlobSettings blobSettings;  
  5.   private readonly Func<CancellationToken, Task> taskToRunWhenLeaseAcquired;  
  6.   ...  
  7.   
  8.   public BlobDistributedMutex(BlobSettings blobSettings,   
  9.            Func<CancellationToken, Task> taskToRunWhenLeaseAquired)  
  10.   {  
  11.     this.blobSettings = blobSettings;  
  12.     this.taskToRunWhenLeaseAquired = taskToRunWhenLeaseAquired;  
  13.   }  
  14.     
  15.   public async Task RunTaskWhenMutexAcquired(CancellationToken token)  
  16.   {  
  17.     var leaseManager = new BlobLeaseManager(blobSettings);  
  18.     await this.RunTaskWhenBlobLeaseAcquired(leaseManager, token);      
  19.   }   
  20.   ...  


代码示例中的RunTaskWhenMutexAquired上述方法调用以下代码示例来实际获得的租赁所示的RunTaskWhenBlobLeaseAcquired方法。该RunTaskWhenBlobLeaseAcquired方法异步运行。如果租赁的成功获取,角色实例已经当选的领导者。该taskToRunWhenLeaseAcquired委托的目的是为了执行协调其它角色实例的工作。如果未取得租赁,另一个角色实例已当选为领导人和当前角色实例仍然是一个下属。注意,TryAcquireLeaseOrWait方法是使用BlobLeaseManager对象获取租赁一个辅助方法。

 

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. ...  
  2.  private async Task RunTaskWhenBlobLeaseAcquired(  
  3.    BlobLeaseManager leaseManager, CancellationToken token)  
  4.  {  
  5.    while (!token.IsCancellationRequested)  
  6.    {  
  7.      // Try to acquire the blob lease.   
  8.      // Otherwise wait for a short time before trying again.  
  9.      string leaseId = await this.TryAquireLeaseOrWait(leaseManager, token);  
  10.   
  11.      if (!string.IsNullOrEmpty(leaseId))  
  12.      {  
  13.        // Create a new linked cancellation token source so that if either the   
  14.        // original token is cancelled or the lease cannot be renewed, the  
  15.        // leader task can be cancelled.  
  16.        using (var leaseCts =   
  17.          CancellationTokenSource.CreateLinkedTokenSource(new[] { token }))  
  18.        {  
  19.          // Run the leader task.  
  20.          var leaderTask = this.taskToRunWhenLeaseAquired.Invoke(leaseCts.Token);            
  21.          ...  
  22.        }  
  23.      }  
  24.    }  
  25.  }  
  26.  ...  


 

由领导开始的任务还异步执行。虽然这个任务正在运行,下面的代码示例所示的RunTaskWhenBlobLeaseAquired方法周期性地尝试续订租约。这个动作有助于确保该角色实例保持领先。在简单解决方案中,续订请求之间的延迟小于对租赁期限,以防止另一角色实例当选的领导人指定的时间。如果更新失败,以任何理由,任务被取消。如果租用未能被更新或任务被取消(可能为角色实例关停的结果),租赁被释放。在这一点上,这个或另一个角色实例可能被选为领导者。下面的代码提取物显示出此过程的一部分。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. ...  
  2.   private async Task RunTaskWhenBlobLeaseAcquired(  
  3.     BlobLeaseManager leaseManager, CancellationToken token)  
  4.   {  
  5.     while (...)  
  6.     {  
  7.       ...  
  8.       if (...)  
  9.       {  
  10.         ...  
  11.         using (var leaseCts = ...)  
  12.         {  
  13.           ...  
  14.           // Keep renewing the lease in regular intervals.   
  15.           // If the lease cannot be renewed, then the task completes.  
  16.           var renewLeaseTask =   
  17.             this.KeepRenewingLease(leaseManager, leaseId, leaseCts.Token);  
  18.   
  19.           // When any task completes (either the leader task itself or when it could  
  20.           // not renew the lease) then cancel the other task.  
  21.           await CancelAllWhenAnyCompletes(leaderTask, renewLeaseTask, leaseCts);  
  22.         }  
  23.       }  
  24.     }  
  25.   }  
  26.   ...  
  27. }  


 

该KeepRenewingLease方法是使用BlobLeaseManager对象续租另一个helper方法。该CancelAllWhenAnyCompletes方法取消指定为前两个参数的任务。

图1示出了BlobDistributedMutex类的功能。

图1 - 使用BlobDistributedMutex类选出一个领导者和运行协调操作的任务


下面的代码示例显示了如何使用BlobDistributedMutex类的辅助角色。此代码获取租赁了一个名为MyLeaderCoordinatorTask在开发的仓储租赁容器中的blob,并指定在MyLeaderCoordinatorTask方法定义的代码应该运行,如果角色实例当选的领导人。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. var settings = new BlobSettings(CloudStorageAccount.DevelopmentStorageAccount,   
  2.   "leases", "MyLeaderCoordinatorTask");  
  3. var cts = new CancellationTokenSource();  
  4. var mutex = new BlobDistributedMutex(settings, MyLeaderCoordinatorTask);  
  5. mutex.RunTaskWhenMutexAcquired(this.cts.Token);  
  6. ...  
  7.   
  8. // Method that runs if the role instance is elected the leader  
  9. private static async Task MyLeaderCoordinatorTask(CancellationToken token)  
  10. {  
  11.   ...  
  12. }  


 

请注意有关样品溶液中的以下几点:
BLOB是一个潜在的单点故障。如果Blob服务不可用,或的blob是人迹罕至,领导者将无法续租,并没有其他的作用,比如将能够获得租约。在这种情况下,没有作用,例如将能够充当领导者。然而,的blob服务被设计为弹性的,所述的blob的服务,以便彻底失败被认为是极不可能的。
•如果被领导者摊位正在执行的任务,领导者可能会继续续租,防止任何其他角色实例从获得租约,并接管了领导作用,以协调任务。在现实世界中,领导者的健康应该频繁地进行检查。
•在选举过程具有不确定性。你不能做任何假设哪个角色实例将得到的blob租约,成为领导者。
•不应使用任何其他目的用作的blob租赁的目标的的blob。如果一个角色实例尝试将数据存储在该的blob,该数据将不能被访问,除非该角色实例是领导者和持有的的blob租约。

本文翻译自MSDN:http://msdn.microsoft.com/en-us/library/dn568104.aspx

posted on 2015-09-01 18:06  Yudar  阅读(312)  评论(0编辑  收藏  举报