我正在研究我的bc论文项目,该项目应该是用scala和Akka编写的Minecraft服务器。服务器应该可以轻松地部署在云中或集群上(不确定我是否使用正确的术语......它应该在多个节点上运行)。然而,我是akka的新手,我一直想知道如何实现这样的事情。我现在想弄清楚的问题是如何在不同节点上的演员之间共享状态。我的第一个想法是让一个Camel actor从minecraft客户端读取tcp流,然后将其发送到负载均衡器,负载均衡器将选择一个处理请求的节点,然后通过tcp向客户端发送一些响应。假设我有一个AuthenticationService实现actor,它检查用户提供的凭据是否有效。每个节点都有这样的actor(或者可能更多),并且所有actor都应该始终拥有完全相同的用户数据库(或状态)。我的问题是,保持这种状态的最佳方法是什么?我想出了一些我能想到的解决方案,但我没有做过这样的事情,所以请指出错误:
解决方案#1:将状态保存在数据库中。对于此身份验证示例,这可能非常有效,其中state仅由用户名和密码列表表示,但在状态包含无法轻易分解为整数和字符串的对象的情况下,它可能不起作用。
解决方案#2:每当有某个演员要求改变它的状态时,演员在处理完请求后,会向同一类型的所有其他演员改变他们的变更信息。根据原始演员发送的信息说明。这看起来非常低效而且相当笨拙。
解决方案#3:让某个节点作为一种状态节点,其中会有代表整个服务器状态的actor。除了这样的节点中的actor之外的任何其他actor都没有状态,并且每次他们需要一些数据时都会询问“状态节点”中的actor。这似乎也是低效的,有点不容错误。
所以你有它。只有我真正喜欢的解决方案才是第一个,但就像我说的那样,它可能仅适用于非常有限的问题子集(当状态可以分解为redis结构时)。来自更有经验的大师的任何回应都会非常受欢迎。 此致,Tomas Herman
答案 0 :(得分:4)
解决方案#1可能很慢。此外,它是一个瓶颈和单点故障(意味着如果具有数据库的节点发生故障,应用程序将停止工作)。解决方案#3也有类似的问题。
解决方案#2不像看起来那么简单。首先,这是一个单点故障。其次,读取或写入没有原子性或其他排序保证(例如规则性),除非你执行total order broadcast(比常规广播更昂贵)。事实上,大多数分布式寄存器算法都会在幕后进行广播,所以虽然效率低下,但可能是必要的。
根据您的描述,您需要分布式寄存器的原子性。原子性是什么意思?原子性意味着并发读取和写入序列中的任何读取或写入都会出现在单个时间点。 非正式,在解决方案#2中,单个actor持有一个寄存器,这保证了如果2个后续写入W1然后W2到寄存器发生(意味着2个广播),那么没有其他actor读取值从寄存器中读取它们的顺序与第一个W1不同,然后是W2(它实际上比它更复杂)。如果您通过几个后续广播的示例,其中消息在不同时间点到达目的地,您将看到根本不保证这样的订购属性。
如果排序保证或原子性不是问题,那么某种基于八卦的算法可能会将变化慢慢传播到所有节点。这可能对你的例子没有多大帮助。
如果你想要完全容错和原子,我建议你阅读这篇关于Rachid Guerraoui和LuísRodrigues的可靠分布式编程的book,或者与分布式寄存器抽象相关的部分。这些算法建立在消息传递通信层之上,并维护支持读写操作的分布式寄存器。您可以使用此类算法来存储分布式状态信息。但是,它们不适用于数千个节点或大型集群,因为它们不能扩展,通常在节点数量上具有复杂多项式。
另一方面,您可能不需要在所有节点上复制分布式寄存器的状态 - 在节点的子集(而不是仅仅一个节点)上复制它,并访问那些节点以进行读取或写入它,提供一定程度的容错(只有当整个节点子集失败时,寄存器信息才会丢失)。您可以调整本书中的算法来实现此目的。