8

我正在研究我的 bc 论文项目,它应该是一个用 scala 和 Akka 编写的 Minecraft 服务器。服务器应该可以轻松地部署在云中或集群上(不确定我是否使用了正确的术语......它应该在多个节点上运行)。但是,我是akka的新手,我一直想知道如何实现这样的事情。我现在要解决的问题是如何在不同节点上的参与者之间共享状态。我的第一个想法是让 Camel actor 从 minecraft 客户端读取 tcp 流,然后将其发送到负载均衡器,负载均衡器将选择一个处理请求的节点,然后通过 tcp 向客户端发送一些响应。假设我有一个 AuthenticationService 实现参与者,它检查用户提供的凭据是否有效。每个节点都会有这样的参与者(或者可能更多),并且所有参与者都应该始终拥有完全相同的用户数据库(或状态)。我的问题是,保持这种状态的最佳方法是什么?我想出了一些我能想到的解决方案,但我还没有做过这样的事情,所以请指出错误:

解决方案#1:将状态保存在数据库中。这对于这个身份验证示例可能非常有效,其中状态仅由用户名和密码列表表示,但在状态包含不能轻易分解为整数和字符串的对象的情况下,它可能不起作用。

解决方案#2:每次对某个参与者的请求会改变其状态时,该参与者将在处理该请求后,将有关更改的信息广播给所有其他相同类型的参与者,这些参与者将根据原始演员发送的信息。这似乎非常低效且相当笨拙。

解决方案#3:让某个节点充当某种状态节点,其中会有代表整个服务器状态的参与者。除了此类节点中的参与者之外,任何其他参与者都没有状态,并且每次需要一些数据时都会询问“状态节点”中的参与者。这似乎也效率低下,而且有点防错。

所以你有它。我真正喜欢的唯一解决方案是第一个,但就像我说的那样,它可能只适用于非常有限的问题子集(当状态可以分解为 redis 结构时)。更有经验的大师的任何回应都会非常感激。问候,托马斯·赫尔曼

4

1 回答 1

4

解决方案#1 可能很慢。此外,它是一个瓶颈和单点故障(意味着如果带有数据库的节点发生故障,应用程序将停止工作)。解决方案#3 有类似的问题。

解决方案#2 并不像看起来那么简单。首先,它是单点故障。其次,读取或写入没有原子性或其他顺序保证(例如规律性),除非您进行全顺序广播(这比常规广播更昂贵)。事实上,大多数分布式寄存器算法都会在底层进行广播,因此,虽然效率低下,但它可能是必要的。

根据您的描述,您的分布式寄存器需要原子性。我所说的原子性是什么意思?原子性意味着在并发读写序列中的任何读取或写入看起来好像它发生在单个时间点。 非正式地,在解决方案 #2 中,单个参与者持有一个寄存器,这保证了如果发生 2 次后续写入 W1 和 W2 到寄存器(意味着 2 次广播),那么没有其他从寄存器读取值的参与者会将它们读入顺序不同于第一个 W1,然后是 W2(实际上比这更复杂)。如果您查看几个后续广播的示例,其中消息在不同时间点到达目的地,您会发现根本无法保证这样的排序属性。

如果排序保证或原子性不是问题,则某种基于八卦的算法可能会起到将更改缓慢传播到所有节点的技巧。这在您的示例中可能不会很有帮助。

如果您想要完全容错和原子,我建议您阅读Rachid Guerraoui 和 Luís Rodrigues 撰写的关于可靠分布式编程的书,或者与分布式寄存器抽象相关的部分。这些算法建立在消息传递通信层之上,并维护一个支持读写操作的分布式寄存器。您可以使用这样的算法来存储分布式状态信息。但是,它们不适用于数千个节点或大型集群,因为它们无法扩展,通常在节点数量上具有复杂性多项式。

另一方面,您可能不需要在所有节点上复制分布式寄存器的状态 - 将其复制到节点的子集(而不仅仅是一个节点)并访问那些以从中读取或写入,提供一定程度的容错(只有当整个节点子集发生故障时,寄存器信息才会丢失)。你可以调整书中的算法来达到这个目的。

于 2011-03-31T17:20:50.180 回答