我对许多人将在办公室使用的应用程序有以下要求 - 没有服务器组件。客户端应用程序的所有实例都应以某种方式在它们之间进行协商,以确定哪个客户端将承担服务器角色。客户应该通过IP进行自己的沟通。
如果客户端应用程序出现故障,另一个客户端必须以无缝方式接管。我知道拥有一台服务器会更简单。但是因为应用程序必须非常有弹性,所以不想让服务器停机(甚至是其备份)的风险,而是依赖于这种混合网状连接,其中服务器角色从客户端跳到客户端。
我认为我的应用连接已关闭。基本上,当应用程序启动时,它会通过UDP(通过预定义的IP地址,所有内容监听或通过UDP广播)宣布自己。从那时起,沟通以类似的方式进行。
我遇到问题的部分是如何在客户端之间进行协商/自组织以选择具有服务器角色的客户端。如何可靠地检测到客户端已经关闭,然后必须进行新的协商。最后的困难是复制客户端使用服务器角色累积的数据。
我在c#中创建了一个原型,用于通信并尝试复制数据,但协商部分(特别是与客户端故障相结合)。
最初我认为这就是ZeroConf(又名Bonjour)所做的。但那只是宣布可用的网络服务。
无论如何,我不想重新发明,我不能成为第一个想要这样做的人。所以我的问题:
答案 0 :(得分:6)
在一组机器中选择服务器,无论这些机器是否也是客户机,都是一个非常重要的问题。这被称为领导人选举。你应该阅读的开创性工作是Leslie Lamport的The Part Time Parliament,它描述了Paxos协议。 Google利用Paxos开发了一个名为Chubby的系统,该系统可以满足您的描述。
那就是说,你应该看看像Apache ZooKeeper这样的系统,它是分布式领导者选举的开源(虽然是Java)实现,更广泛地说,分布式锁管理已在大负荷。 Hadoop分布式数据存储和计算平台,特别是HBase,一个在Hadoop上运行的分布式数据库,大量使用ZooKeeper来决定一组服务器中的“谁负责”。这样他们中的任何一个都可能会失败,而其他人则决定谁接管了这份工作。
正如我前面提到的,领导人选举充满了错误。做对很难。我已经在C#中实现了“为了好玩”的paxos六次,并且所有我的实现都有bug。
答案 1 :(得分:4)
因此,您目前拥有一个系统,其中LAN上的每个客户端都将通过UDP向LAN的其余部分宣布自己。其中一个客户端应用程序是一个“服务器”,并有一些额外的命令&控制权本身就是客户本身。
这肯定不是一个新想法。你想要的是在最初的“我在这里”连接通信期间添加一些额外的谈话。当一个新客户端“在这里我”到局域网的其他部分时,如果有服务器,服务器应该说“欢迎,我是服务器”,新的客户端应用程序现在知道哪个客户端充当了服务器。所有其他客户也应该说“嗨”。如果没有服务器,新客户端应该首先重复“hello”(它毕竟是UDP;你不得不期望收到一些消息),如果没有人响应,这个新客户端是唯一一个网络,默认为“服务器”。如果有其他人但没有人声称是服务器,客户可以“自己讨论”以确定新的服务器。
除此之外,服务器副本应定期(可能每3-5秒)向所有人喊“我还在这里”;这被称为“心跳”消息,是双向“ping”验证方法的一种非常常见的替代方法。
如果服务器应用程序(或任何副本,真的)正常关闭,它应该喊出“再见所有人,找出谁是下一个服务器”。其余的客户可以互相讨论。如果作为服务器的客户端崩溃,客户端将错过服务器的“心跳”消息,询问“谁是服务器”,如果没有人仍然响应,客户端将相互讨论。
现在,客户“彼此讨论”可以像你喜欢的那样简单或复杂。最简单的是任何一个客户说“好了,我现在服务器”成为服务器。您可能必须在消息中包含某种时间,以便如果另一台计算机同时说出来,客户端可以说“客户端15先说出来,所以我们就和他一起去”。客户可以“投票”;让每个客户端与所有其他客户端通信以确定该客户端与所有其他客户端之间的标称延迟,并且该客户端将“投票”以获得最低延迟连接(除非客户端发现它是唯一的一个,否则客户端可以自行投票)。大多数选票获胜。
或者,作为“心跳”消息的一部分,服务器可以说“如果我失败了,我的继任者就是客户X”;如果错过了心跳并且随后的“你还在那里,服务器”的消息没有得到响应,客户可以说“国王死了!King Client X万岁!”。
了解必要时,选择“权威”客户端成为服务器的全客户端系统中的这一层治理将大大增加客户端通信的复杂性。此外,虽然您使用UDP协议有助于快速通信,但UDP消息始终会发生冲突;如果你正在和另一个人说话时说话,你的信息就会发生冲突。因此,我建议在此软件中使用TCP而不是UDP进行大多数通信,在该软件中需要听取特定客户端的信息。这是客户端的任何直接询问(“你还在那里,服务器吗?”),无论你用什么过程让客户决定新服务器是谁等等。
答案 2 :(得分:3)
为什么你需要协商服务器的角色?想一想这一秒。如果每个“客户端”可以处理在客户端启动的工作的“服务器”职责,那么所有客户端和服务器都在一定程度上处理。然后唯一的问题是在两个客户端处理相同的状态时协商客户端之间的持久状态复制和处理并发性(从我的角度来看,最困难的部分是警告其他客户端当一个客户端“保存”时状态已经改变数据并让其他客户处理该州开辟了一种方法来解决冲突 - 如果最后总是获胜,可能不是问题,但这种情况很少见。)
如果你真的要进行网格划分,那么每个客户端应该能够独立于其他客户端处理工作,并且只将更改传递给持久状态,以便副本匹配。
以上假设多于一个人同时使用相同状态的次数是有限的。如果这是正常情况,那么你将不得不弄清楚你的一些“服务器”逻辑。