自组织应用程序

19

我有一个应用程序的需求,许多人将在办公室使用它 - 没有服务器组件。所有客户端应用程序实例都应以某种方式进行协商,以决定哪个客户端将承担服务器角色。并且客户端应该通过IP相互通信。

如果客户端应用程序崩溃,另一个客户端必须无缝地接管。我知道拥有服务器会简单得多。但由于应用程序必须非常弹性化,所以权力机构不想冒险使服务器(甚至是备份)崩溃,而是依赖于这种混合网状连接,在客户端之间跳转服务器角色。

我认为应用程序连接已经解决了。基本上,当应用程序启动时,它会通过UDP(向所有内容都监听的预定义IP地址或通过UDP广播)宣布自己的存在。从那里开始,通信就以类似的方式进行。

我遇到的问题是如何在客户端之间协商/自组织以选择扮演服务器角色的客户端。以及如何可靠地检测客户端已经停机,然后必须进行新的协商。最后的困难是复制由具有服务器角色的客户端累积的数据。

我在C#中创建了一个原型,它可以通信并尝试复制数据,但是协商部分(特别是与客户端故障耦合的部分)仍有问题。

最初我以为这就是ZeroConf(也称为Bonjour)所做的。但是那只会宣布可用的网络服务。

无论如何,我不想重新发明轮子,我也不可能是第一个想要做到这一点的人。所以我的问题:

  • 是否有实现我上述描述的模式?
  • 如果是,是否有可用的.NET(甚至native)库?
  • 在客户端之间协商服务器角色的好方法是什么?

2
你看过System.Net.PeerToPeer吗?http://msdn.microsoft.com/en-us/library/system.net.peertopeer.aspx - Chris Shain
@ChrisShain 这可能是个不错的东西。我之前完全不知道它的存在。谢谢。 - AngryHacker
当然。另外请看下面关于领导者选举的回答。那是一个棘手的问题。 - Chris Shain
1
@ChrisShain 推荐了 Zookeeper,我也赞同。尽管服务器是 Java,但有一个相当不错的 .NET 客户端正在被加入到分发中,因此应该会得到很好的支持。它确实在某种程度上违反了无服务器组件的要求,但高度弹性的集中式协调服务类似于 DNS,可能会被权力机构接受。 - manku
3个回答

6

在一组机器中选择服务器,无论这些机器是否也是客户端,都是一个非常棘手的问题。这被称为领导者选举。你应该阅读的开创性工作是Leslie Lamport的《兼职议会》,其中描述了Paxos协议。Google利用Paxos开发了一个名为Chubby的系统,它可以达到你所描述的目的。

那么,您应该考虑使用像Apache ZooKeeper这样的系统,它是一个开源(尽管是Java)的分布式领导人选举实现,更广泛地说,分布式锁管理在大规模负载下经过了彻底测试。Hadoop分布式数据存储和计算平台,特别是运行在Hadoop上的HBase分布式数据库,都大量使用ZooKeeper来决定在一组服务器中“谁在掌控”。这样,其中任何一个服务器都可以停机,其他服务器会自行决定谁接管工作。
正如我之前提到的,领导人选举充满了错误。很难做到完美无缺。我已经用C#实现了半打Paxos算法“为了好玩”,但所有的实现都有漏洞。

4
所以,您目前拥有的系统是每个局域网上的客户端将通过UDP向其他客户端宣告自己的存在。其中一个客户端应用程序是“服务器”,除了作为客户端之外还具有额外的命令和控制功能。
这并不是一个新想法。您需要在初始的“我在这里”的连接通信期间添加一些额外的对话。当新客户端向整个局域网喊叫“我在这里”时,如果存在服务器,则服务器应该说“欢迎,我是服务器”,现在新的客户端应用程序知道哪个客户端充当服务器了。所有其他客户端也应该说“嗨”。如果没有服务器,新客户端应该先重复“你好”(毕竟这是UDP;您必须预期有些消息无法收到),如果没有人回应,则此新客户端是默认的网络中唯一的客户端,并且成为“服务器”。如果有其他人但没有人声称自己是服务器,则客户端可以“相互讨论”以确定一个新的服务器。
除此之外,服务器副本应周期性地(可能每3-5秒)向大家喊出“我还活着”的信息;这被称为“心跳”消息,是验证的双向“ping”方法的非常普遍的替代方法。
如果服务器应用程序(或任何副本)正常关闭,它应该向所有人喊出“再见,大家自己决定谁是下一个服务器”。其余客户端随后可以相互讨论。如果充当服务器的客户端崩溃,则客户端将错过服务器的“心跳”消息,并询问“谁是服务器”,如果仍然没有响应,则客户端将相互讨论。
现在,客户端“相互讨论”可以很简单,也可以很复杂。最简单的方法是,无论哪个客户端说“好的,我现在是服务器”,都成为服务器。您可能需要在消息中包含某种时间,以便如果另一台计算机同时说出它,客户端可以说“好吧,第15个客户端先说了,所以我们跟他走”。客户端可以“投票”;每个客户端都可以与所有其他客户端交谈,以确定该客户端与所有其他客户端之间的名义延迟,该客户端将为最低延迟连接“投票”(除非发现它是唯一的客户端,否则不得为自己投票)。多数票获胜。
或者,作为其“心跳”消息的一部分,服务器可以说“如果我倒下了,我的继任者是客户端X”;如果错过了心跳,并且来自客户端的随后的“你还在那里,服务器”消息没有得到回复,客户端可以说“国王已经死了!长命王客户端X!”
理解到,在一个全客户端系统中,为了挑选一个“权威”的客户端成为服务器,这一层治理的必要性将会极大地增加客户端通信的复杂性。此外,虽然您使用UDP协议可以实现快速通信,但UDP消息经常发生碰撞;如果你在说话时另一个人也在说话,你们的消息就会碰撞。因此,我建议在需要特定客户端被听到的这个软件中,大多数通信使用TCP而不是UDP。这包括任何直接询问客户端(“服务器,你还在那里吗?”),无论你使用什么过程来让客户端决定谁是新服务器等等。

1
“UDP消息总是发生冲突”是什么意思?也就是说,它们无法到达目的地吗?我已经使用UDP很长时间了,但从未见过这种情况。是否存在特定条件会导致“冲突”? - AngryHacker
1
@KeithS是正确的,但没有深入到足够的程度。即使使用TCP,您的系统也有许多方法可以“意外”选举多个服务器。想象一下,您拥有以下拓扑结构的网络(客户端1,2,3)->(交换机1)->(交换机2)<-(交换机3)<-(客户端3,4,5)。当客户端1是服务器且(交换机2)被关闭时会发生什么?在不使用算法上正确的领导者选举协议的系统中,将选举Clients 3,4,5之一,然后您就有了2个服务器。糟糕。 - Chris Shain
如果开关2被关闭,那么现在有两个局域网彼此无法看到。被切断的网络分支上的客户端必须选择自己的领导者。现在,如果开关2再次打开,两个服务器将听到彼此的“心跳”,说“等一下,我是服务器”,然后应该在它们之间进行协商(假设客户端1是比较长时间运行的,假设它在开关关闭时没有崩溃,则应该获胜)。 - KeithS
1
是的,假设在网络分区期间有2个服务器是可以接受的,那就没问题了。在绝大多数情况下,这样做是不可接受的,因为可能会导致完整性违规(主要是在当前服务器视图是权威的情况下,因此拥有两个服务器可能会导致关于世界状态的不可解决的冲突)。 - Chris Shain
1
@KeithS:UDP确实有错误检查。头部中有一个校验和:http://www.ietf.org/rfc/rfc768.txt 此外,UDP并不比任何发送到物理层的东西更容易发生冲突。TCP和UDP数据包都可能因各种原因丢失,不同之处在于TCP会重新发送丢失的数据包,而UDP则被设计为“发送并忘记”。实际上,如果您想要的话,可以在UDP之上构建TCP。但我不建议这样做。 - grieve
显示剩余3条评论

3
为什么你需要协商服务器角色呢?想一想,如果每个“客户端”都能处理在客户端启动的“服务器”工作,那么所有人都能在某种程度上处理客户端和服务器。那么唯一的问题就是协商在客户端之间复制持久状态并处理并发时两个客户端正在处理相同状态的情况(从我的角度来看,最难的部分是通知其他客户端状态已经改变当一个客户端“保存”数据并使其他客户端正在处理的状态打开一个解决冲突的方法 - 如果后进先出可能不是问题,但这是罕见的)。
如果您真的要进行网状连接,则每个客户端应能够独立处理其自己的工作,而无需与其他客户端通信以便让拷贝匹配。
上述假设是在少数情况下多人同时使用相同状态。 如果这是正常情况,则您将不得不想出一些“服务器”逻辑。

为什么你需要协商服务器的角色呢?因为只有一个客户端可以访问所有人都需要的宝贵资源。 - AngryHacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接