我目前正在开发分布式系统,我们必须实现某种Leader Election。 问题是我们希望避免所有计算机必须相互了解 - 但只有领导者。有没有一种快速的方法,我们可以使用例如广播来实现我们想要的东西?
或者,我们是否只需知道至少一个,以进行良好的领导人选举?
可以假设所有计算机都在同一个子网上。
感谢您的帮助。
答案 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之上构建系统。
使用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创建一个临时节点,可读取所有节点。只要在群集状态发生变化时,仅使顶部节点可供读取,就可以覆盖此行为。
仅当大量节点同时启动(以毫秒为单位)并且中间系统花费很长时间才能返回微小结果时,并发才可能成为问题。