Lun*_*kon 43 akka playframework playframework-2.0 akka-cluster
我最近一直试图围绕Akka和基于演员的系统的概念.虽然我对Akka基本原理有很好的理解,但现在我仍然在集群和远程演员方面遇到一些问题.
我将尝试使用Play Framework 2.0附带的WebSocket聊天示例来说明问题:有一个持有WebSockets并保留当前连接用户列表的actor.演员基本上在技术上和逻辑上代表聊天室.只要在单个服务器上运行单个聊天室,这就完美无缺.
现在,我正在尝试理解当我们讨论在服务器群集上运行的许多动态聊天室(可以随时打开/关闭的新房间)时添加或删除单个节点时如何扩展此示例根据目前的需求).在这种情况下,用户A可以连接到服务器1,而用户B连接到服务器2.两者可能在同一个聊天室中进行通话.在每个服务器上仍然会有一个actor(对于每个聊天室?),它保存WebSocket实例以接收和发布事件(消息)给正确的用户.但从逻辑上讲,服务器1或服务器2上只应有一个聊天室角色,其中包含当前连接用户(或类似任务)的列表.
您将如何实现这一目标,最好是在"纯akka"中,而不添加ZeroMQ或RabbitMQ等额外的消息传递系统?
这是我到目前为止所提出的,请告诉我这是否有意义:
如果服务器2出现故障,则必须以某种方式在服务器2上重新创建/移动聊天室角色,尽管这不是我现在主要关注的问题.我最想知道演员如何在各种基本上独立的机器上进行动态发现,可以使用Akka的工具集来完成.
我一直在看Akka的文档很长一段时间了,所以也许我错过了这里显而易见的.如果是的话,请赐教:-)
th3*_*m0r 12
我正在开发一个私人项目,它基本上是聊天室示例的一个非常扩展的版本,我也有akka的启动问题和整个"分散"思维.所以我可以告诉你我是如何"解决"我扩展的聊天室:
我想要一台可以轻松部署多次的服务器而无需额外的配置.我使用redis作为所有开放用户会话(其ActorRefs的简单序列化)和所有聊天室的存储.
服务器具有以下actor:
WebsocketSession
:保存与一个用户的连接并处理来自用户的请求并转发来自系统的消息.ChatroomManager
:这是中央广播公司,它部署在服务器的每个实例上.如果用户想要向聊天室发送消息,则WebSocketSession-Actor将所有信息发送到ChatroomManager-Actor,然后ChatroomManager-Actor将消息广播给聊天室的所有成员.所以这是我的程序:
actorFor
-method 一起使用的绝对路径)检索聊天室的用户列表,然后将消息发送给每个session-actor.然后这些session-actors写入他们的websockets.在每个ChatroomManager-actor中,我做了一些ActorRef
缓存,这提供了额外的速度.我认为这与您的方法不同,特别是这些ChatroomManagers处理所有聊天室的请求.但是在一个聊天室中有一个演员是我想要避免的单点故障.这将导致更多消息,例如:
如果用户A想要与用户B通话,则他们都必须通过服务器1上的聊天室 - 演员进行通信.
此外,我使用akka的功能(如(循环) - 路由器在每个系统上创建ChatroomManager-actor的多个实例来处理许多请求.
我花了几天时间来建立整个akka远程基础设施以及序列化和redis.但是现在我能够创建任意数量的服务器应用程序实例,这些实例使用redis来共享ActorRef
(用ip +端口序列化为绝对路径).
这可能对你有所帮助,我对新问题持开放态度(请不要关注我的英语;).
Sof*_*mes 10
跨多台机器扩展的关键是尽可能保持可变状态.虽然您可以使用分布式缓存来协调所有节点的状态,但这会在扩展到大量节点时为您提供同步和瓶颈问题.理想情况下,应该有一个演员知道聊天室中的消息和参与者.
问题的核心是,如果一个聊天室由在一台机器上运行的单个actor表示 - 或者实际上是否存在这样的房间.诀窍是使用标识符(例如聊天室的名称)来路由与给定聊天室相关的请求.计算名称的哈希值,并根据数字从n个框中选择一个.该节点将了解其当前的聊天室,并可以安全地为您找到或创建正确的聊天室演员.
您可以查看以下关于在Akka中进行群集和扩展的博客文章:
http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-1/
http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-2/
我会使用Zookeeper + Norbert来了解哪些主机上下起伏:
http://www.ibm.com/developerworks/library/j-zookeeper/
现在,我的聊天室服务器场中的每个节点都可以知道逻辑集群中的所有主机.当节点脱机(或联机)时,它们将获得回调.任何节点现在都可以保留当前集群成员的排序列表,散列聊天室ID,并按列表大小调整mod,以获取列表中的索引,该列表是应该托管任何给定聊天室的节点.我们可以添加1和rehash来选择第二个索引(需要一个循环,直到获得一个新的索引)来计算第二个主机以保留聊天室的第二个副本以实现冗余.在两个聊天室主机中的每一个上都是一个聊天室演员,它只是将所有聊天消息转发给每个Websocket演员,这是一个聊天室成员.
现在我们可以通过活跃的聊天室演员和自定义的Akka路由器发送聊天消息.客户端只发送一次消息,路由器将执行散列模式并发送给两个远程聊天室演员.我会使用twitter雪花算法为正在发送的消息生成唯一的64位ID.请参阅以下链接中代码的nextId()方法中的算法.可以使用norbert属性设置datacenterId和workerId,以确保不会在不同服务器上生成冲突ID:
现在,每个消息的两个副本将通过两个活动的聊天室演员中的每一个发送到每个客户端端点.在每个Websocket客户端actor上,我将取消位雪花ID的位,以了解发送消息的datacenterId + workerId号,并跟踪从群集中每个主机看到的最高聊天消息号.然后我会忽略任何不高于给定发送方主机的给定客户端已经看到的消息.这将重复删除通过两个活跃的聊天室演员传入的消息.
到现在为止还挺好; 我们会有弹性消息传递,如果任何节点死亡,我们不会丢失一个幸存的聊天室副本.消息将自动通过第二个聊天室不间断地流动.
接下来,我们需要处理从群集中退出或被添加回群集的节点.我们将在每个节点内获得一个norbert回调,以通知我们集群成员资格的变化.在此回调中,我们可以通过自定义路由器发出一条akka消息,说明新的成员资格列表和当前的主机名.当前主机上的自定义路由器将看到该消息并更新其状态以了解新的集群成员资格,以计算新的节点对以通过发送任何给定的聊天室流量.这种对新集群成员身份的确认将由路由器发送到所有节点,以便每个服务器都可以跟踪所有服务器何时赶上成员身份更改并正在正确发送消息.
成员变更后,幸存的聊天室可能仍然有效.在这种情况下,所有节点上的所有路由器将继续正常发送给它,但也会以推测方式向新的第二个聊天室主机发送消息.第二个聊天室可能尚未启动,但这不是问题,因为消息将通过幸存者流动.如果在成员资格更改后,幸存的聊天室不再处于活动状态,则所有主机上的所有路由器将首先发送给三台主机; 幸存者和两个新节点.可以使用akka死亡监视机制,以便所有节点最终可以看到幸存的聊天室关闭,以通过两个主机返回路由聊天流量.
接下来,我们需要根据具体情况将聊天室中的聊天室迁移到一个或两个新主机上.冲浪的聊天室演员将在某个时候获得一条消息,告诉它新的集群成员资格.它将首先将聊天室成员资格的副本发送到新节点.此消息将在新节点上创建具有正确成员身份的聊天室actor的新副本.如果幸存者不再是应该容纳聊天室的两个节点之一,它将进入退役模式.在退役模式下,它只会将任何消息转发到新的主节点和辅助节点而不转发给任何聊天室成员.Akka消息转发是完美的.
退役聊天室将监听来自每个节点的norbert群集成员身份确认消息.最终,它将看到集群中的所有节点都已确认新的集群成员资格.然后它知道它将不再接收任何进一步转发的消息.它可以自杀.Akka hotswapping非常适合实施退役行为.
到现在为止还挺好; 我们有一个弹性的消息传递设置,它不会因节点崩溃而丢失消息.在集群成员资格发生变化的时刻,我们将获得一个内部流量峰值,以便将聊天室复制到新节点.我们还有一些剩余的节点转发消息到节点,直到所有服务器都赶上了哪些聊天室移动了两个服务器.如果我们想要扩展系统,我们可以等到用户流量的低点,然后打开一个新节点.聊天室将自动在新节点上重新分配.
以上描述基于阅读以下论文并将其翻译成akka概念:
https://www.dropbox.com/s/iihpq9bjcfver07/VLDB-Paper.pdf