分布式系统:领导者选举

时间:2013-04-17 09:07:21

标签: algorithm protocols distributed-system

我目前正在开发分布式系统,我们必须实现某种Leader Election。 问题是我们希望避免所有计算机必须相互了解 - 但只有领导者。有没有一种快速的方法,我们可以使用例如广播来实现我们想要的东西?

或者,我们是否只需知道至少一个,以进行良好的领导人选举?

可以假设所有计算机都在同一个子网上。

感谢您的帮助。

5 个答案:

答案 0 :(得分:10)

  

问题在于我们希望避免所有计算机必须彼此了解 - 但只有领导者。

领导者选举是从一组潜在的领导者候选人中挑选一名领导者的问题。将其视为具有两个必需属性:活跃安全。在这里,活跃意味着"大多数时候,有一个领导者",而安全意味着"有零或一个领袖&#34 ;.我们将考虑如何使用广播在您的示例中解决此安全属性。

假设每个节点都有一个唯一的ID,我们选择一个简单(破碎)的算法。每个节点广播其ID并侦听。当收到比自己更高的ID时,它将停止参与。如果它收到的ID比自己的ID低,它会再次发送自己的广播。假设一个同步网络,每个人收到的最后一个ID是领导者的ID。现在,介绍一个网络分区。该协议将很快继续在分区的任何一方,两位领导人将被选举。

这个破坏的协议确实如此,但对所有可能的协议也是如此。如果您不知道(至少)存在多少个节点,您如何区分可以与之通信的节点和不存在的节点之间的区别?因此,第一个安全结果:您需要知道存在多少个节点,或者您无法确保只有一个领导者。

现在,让我们放松我们的安全约束是一个概率问题:"可能有零个或多个领导者,但大部分时间都有一个" 。这使问题易于处理,广泛使用的解决方案是八卦(流行病协议)。例如,请参阅A Gossip-Style Failure Detection Service,其中讨论了此确切问题的变体。本文主要关注概率正确的故障检测和枚举,但如果你能做到这一点,你也可以做出概率正确的领导者选举。

据我所知,你不能在一般网络中进行安全的非概率性领导者选举,而不至少列举参与者。

答案 1 :(得分:1)

作为一个有趣的“分布式机制”解决方案,我上次看到我推荐Apache zookeeper项目。这是开源解决方案,所以至少你应该能够从那里得到一些想法。此外,它正在深入开发,因此您可以将其重新用作解决方案的一部分。

  

ZooKeeper是一种用于维护配置的集中服务   信息,命名,提供分布式同步,以及   提供团体服务。所有这些服务都用于   某种形式或分布式应用程序。他们每次都是   实施了很多修复bug的工作   竞争条件是不可避免的。因为困难   通常最初实现这些类型的服务   吝啬它们,使它们在变化和变化的情况下变脆   难以管理。即使做得正确,也不同   这些服务的实现会导致管理复杂性   应用程序已部署。

答案 2 :(得分:1)

我建议JGroups解决这个问题 - 假设您正在JVM之上构建系统。

http://www.jgroups.org/

使用LockService确保群集中只有1个节点是领导者。可以将JGroups设置为使用对等锁定或中央锁定 - 这两种方法都适用于您的情况。

有关Clojure实现,请参阅http://withmeta.blogspot.com/2014/01/leader-election-problem-in-elastic.html,有关Java实现,请参阅http://javabender.blogspot.com.au/2012/01/jgroups-lockservice-example.html

答案 3 :(得分:0)

一个实际的解决方案是使用DB作为"会议"点。

如果您已经在使用SQL DB,这个解决方案非常方便,只需要一个新表。如果您正在使用数据库群集,则可以利用其高可用性。

以下是我的实现使用的表:

class SqlLease {
  private ISqlLeaseDal _dal;
  private string _resourceId;
  private string _myId;

  public SqlLease(ISqlLeaseDal dal, string resourceId) {
    _dal = dal;
    _resourceId = resourceId;
    _myId = Guid.NewGuid().ToString();
  }

  class LeaseRow {
      public string ResourceId {get; set;}
      public string OwnerId {get; set;}
      public Datetime Expiration {get; set;}
      public byte[] RowVersion {get; set;}
  }

  public bool TryAcquire(Datetime expiration) {
    expiration = expiration.ToUniversalTime();
    if (expiration < DateTime.UtcNow) return false;
    try {
      var row = _dal.FindRow(_resourceId);
      if (row != null) {
        if (row.Expiration >= DateTime.UtcNow && row.OwnerId != _myId) {
          return false;
        }
        row.OwnerId = _myId;
        row.Expiration = expiration;
        _dal.Update(row);
        return true;
      }
      _dal.Insert(new LeaseRow {
        ResourceId = _resourceId,
        OwnerId = _myId,
        Expiration = expiration,
      });
      return true;
    } catch (SqlException e) {
      if (e.Number == 2601 || e.Number == 2627) return false;
      throw e;
    } catch (DBConcurrencyException) {
      return false;
    }
  }
}

这个想法是每个共享资源有一行。领导者将争夺同一排。

我过度简化的C#实现看起来像这样:

ISqlLeaseDal

SELECT类封装SQL连接和对表的低级访问。

使用合理的截止日期。请记住,如果当前领导者失败,资源将被锁定,直到到期结束。

答案 4 :(得分:0)

@Marc对此进行了很好的描述。我想补充一点。

如果所有参与系统都不能互相了解,那么广播ID(或说时间戳)除非被选为领导者,否则不会透露其状态。 一旦被选为领导者,它现在可以广播计算机的状态,供集群中的所有其他节点连接。

如果参与系统根本不能透露其存在,则必须有一个系统进行通信,例如数据库(如Igor所述),基于TCP的系统或安装位置(动物园管理员选择的方式) 在其中存储了所有机器状态,但最少(或者第一个状态具有读取权限),并且领导者继续将其状态更新到该系统。 如果领导者失败了,那么系统将使下一个节点可供读取,以选择下一个节点作为领导者,以清除最后一个领导者条目。

Zookeeper创建一个临时节点,可读取所有节点。只要在群集状态发生变化时,仅使顶部节点可供读取,就可以覆盖此行为。

仅当大量节点同时启动(以毫秒为单位)并且中间系统花费很长时间才能返回微小结果时,并发才可能成为问题。