自组织应用程序
本文关键字:应用程序 | 更新日期: 2023-09-27 18:22:37
对于许多人将在办公室中使用的应用程序,我有以下要求-无服务器组件。客户端应用程序的所有实例都应该以某种方式在它们之间进行协商,以决定哪个客户端将扮演服务器角色。客户端之间应该通过IP进行通信。
如果客户端应用程序出现故障,则必须由另一个客户端无缝接管。我知道拥有一台服务器会简单得多。但由于该应用程序必须具有很强的弹性,当权者不想冒服务器宕机(甚至备份)的风险,而是依赖于这种混合网格连接,即服务器角色从客户端跳到客户端。
我想我的应用程序连接坏了。基本上,当应用程序启动时,它会通过UDP(向所有东西都监听的预定义IP地址或通过UDP广播)宣布自己。从那时起,通信以类似的方式进行。
我遇到的问题是如何在客户端之间协商/自组织,以选择一个具有服务器角色的客户端。以及如何可靠地检测到客户机故障,然后必须进行新的协商。最后一个困难是复制由具有服务器角色的客户端积累的数据。
我在c#中创建了一个原型,它通信并试图复制数据,但协商部分(尤其是与客户端故障相关的部分)。
起初我以为ZeroConf(又名Bonjour)就是这么做的。但这只是宣布了可用的网络服务。
无论如何,我不想重新发明,我也不能成为第一个想这样做的人。所以我的问题是:
- 有没有一个模式已经实现了我上面描述的内容
- 如果是,是否有可用的.NET(甚至本机)库
- 在客户端之间协商服务器角色的好方法是什么
在一组机器中选择服务器,无论这些机器是否也是客户端,都是一个极其重要的问题。这叫做领导人选举。你应该阅读的开创性著作是莱斯利·兰波特的《兼职议会》,该书描述了帕克索斯协议。谷歌利用Paxos开发了一个名为Chubby的系统,它达到了你所描述的目的。
也就是说,你可能应该看看像Apache ZooKeeper这样的系统,它是分布式领导人选举的开源(尽管是Java)实现,更广泛地说,它是在巨大负载下经过彻底测试的分布式锁管理。Hadoop分布式数据存储和计算平台,特别是运行在Hadoop上的分布式数据库HBase,大量使用ZooKeeper来决定一组服务器中的"负责人"。这样,他们中的任何人都可以下台,其他人自己决定谁来接管这份工作。
正如我前面提到的,领导人选举充满了错误。很难做到正确。我在C#中"为了好玩"实现了六次paxos,我的所有实现都有错误。
因此,您目前有一个系统,在该系统中,LAN上的每个客户端都将通过UDP向LAN的其他部分宣布自己。其中一个客户端应用程序是"服务器",并有一些额外的命令&除了作为客户本身之外,还拥有控制权。
可以肯定的是,这不是一个新想法。你想要的是在最初的"我在这里"连接通信中添加一些额外的谈话。当一个新客户端对局域网的其他部分大喊"我来了"时,如果有服务器,服务器应该说"欢迎,我是服务器",新客户端应用程序现在知道哪个客户端充当服务器。所有其他客户可能也应该说"嗨"。如果没有服务器,新客户端应该首先重复"你好"(毕竟是UDP;你必须期望一些消息不会被接收到),如果没有人响应,这个新客户端是网络上唯一的客户端,默认情况下是"服务器"。如果有其他人但没有人声称自己是服务器,客户端可以"相互讨论"以确定新的服务器。
除此之外,服务器副本应该定期(可能每3-5秒)向每个人大喊"我还在这里";这被称为"心跳"消息,是双向"ping"验证方法的一种非常常见的替代方法。
如果服务器应用程序(或任何副本,真的)正常关闭,它应该大喊"再见,大家,弄清楚谁是下一个服务器"。剩下的客户可以相互讨论。如果充当服务器的客户端崩溃,客户端将错过服务器的"心跳"消息,询问"谁是服务器",如果没有人响应,客户端将相互讨论。
现在,客户之间的"讨论"可以很简单,也可以很复杂。最简单的方法是,无论哪个客户端说"好吧,我现在是服务器",都可以成为服务器。你可能需要在消息中包含一些时间,这样,如果另一台计算机同时说了,客户就可以说"好吧,客户15先说了,所以我们和他一起去"。客户可以"投票";让每个客户端与所有其他客户端进行对话,以确定该客户端和所有其他客户端之间的标称延迟,并且该客户端将"投票"选择延迟最低的连接(除非发现只有它一个,否则任何客户端都不能投票给自己)。大多数选票获胜。
或者,作为其"心跳"消息的一部分,服务器可以说"如果我宕机,我的继任者是客户端X";如果错过了心跳,并且来自客户端的后续"你还在吗,服务器"消息没有得到响应,客户端可以说"国王死了!国王客户端X万岁!"。
要明白,在选择一个"权威"客户端作为服务器的过程中,全客户端系统中的这一层治理必然会大大增加客户端通信的复杂性。此外,当您使用UDP协议有助于快速通信时,UDP消息总是发生冲突;如果你在别人说话的时候说话,你的信息就会发生冲突。因此,我建议在该软件中的大多数通信中使用TCP而不是UDP,在这些通信中,有必要听到特定客户端的声音。这是对客户端的任何直接询问("你还在吗,服务器?"),无论你用什么过程让客户端决定谁是新服务器,等等。
为什么你需要协商服务器的角色?想一想这个。如果每个"客户端"都能处理在客户端启动的工作的"服务器"职责,那么在一定程度上,所有"客户端"同时处理客户端和服务器。然后,唯一的问题是协商客户端之间持久状态的复制,以及在两个客户端处理相同状态时处理并发(从我的角度来看,最困难的部分是在一个客户端"保存"时提醒其他客户端状态已更改数据和让其他客户端处理状态打开了解决冲突的方法——如果最后一个总是获胜,这可能不是问题,但这种情况很少见)。
如果您真的要网格化,那么每个客户端都应该能够独立于其他客户端处理它的工作,并且只传达对持久状态的更改,以便副本匹配。
以上假设一个人在同一时间使用同一状态的次数是有限的。如果这是正常的情况,那么您必须弄清楚一些"服务器"逻辑。