【问题标题】:How can I gather state information from a set of actors using only the actorSystem?如何仅使用 actorSystem 从一组参与者中收集状态信息?
【发布时间】:2015-01-29 16:10:24
【问题描述】:

我正在创建一个参与者系统,它有一个表示某种会话状态的参与者列表。 这些会话由工厂参与者创建(如果性能需要,将来可能会被路由器取代 - 但是这对系统的其余部分应该是透明的)。 现在我想实现一个操作,从我当前存在的每个会话参与者那里获取一些状态信息。 我没有明确的会话列表,因为我想依赖“拥有”会话的参与者系统。我尝试使用演员系统来查找当前的会话演员。问题是我没有找到“使用这种命名模式获取所有演员参考”的方法。我尝试在系统上使用“/”运算符,然后使用 resolveOne - 但迷失在未来类型的迷宫中。

我的基本想法是: - 向所有当前会话参与者发送消息(由我的 ActorSystem 给我的)。 - 等待他们的响应(最好只使用“询问”模式 - 调用此广播器请求/响应的方法只是一个监视响应调试方法,因此阻塞在这里没有问题。 - 然后将响应收集到一个结果中。

在与 Scala 的类型系统进行了一场死战之后,我现在不得不放弃。 这样的事情真的没有办法吗?

【问题讨论】:

    标签: scala akka


    【解决方案1】:

    如果我正确理解了这个问题,那么我可以提供几种方法来实现这一点(当然还有其他方法)。

    选项 1

    在这种方法中,将有一个参与者负责定期唤醒并向所有会话参与者发送请求以获取其当前统计信息。该演员将使用带有通配符的ActorSelection 来实现该目标。如果这种方法的代码大致如下:

    case class SessionStats(foo:Int, bar:Int)
    case object GetSessionStats
    
    class SessionActor extends Actor{
      def receive = {
        case GetSessionStats =>
          println(s"${self.path} received a request to get stats")
          sender ! SessionStats(1, 2)
      }
    }
    
    
    case object GatherStats
    class SessionStatsGatherer extends Actor{
      context.system.scheduler.schedule(5 seconds, 5 seconds, self, GatherStats)(context.dispatcher)
    
      def receive = {
        case GatherStats =>
          println("Waking up to gether stats")
          val sel = context.system.actorSelection("/user/session*")
          sel ! GetSessionStats
    
        case SessionStats(f, b) =>
          println(s"got session stats from ${sender.path}, values are $f and $b")
      }
    }
    

    然后您可以使用以下代码测试此代码:

    val system = ActorSystem("test")
    system.actorOf(Props[SessionActor], "session-1")
    system.actorOf(Props[SessionActor], "session-2")
    
    system.actorOf(Props[SessionStatsGatherer])
    
    Thread.sleep(10000)
    system.actorOf(Props[SessionActor], "session-3")
    

    因此,使用这种方法,只要我们使用命名约定,我们就可以使用带有通配符的演员选择来始终找到所有会话演员,即使他们不断地来(开始)和去(停止)。

    选项 2

    有点类似的方法,但在这个方法中,我们使用一个集中的 Actor 来生成会话 Actor 并充当它们的监督者。这个中心参与者还包含定期轮询统计信息的逻辑,但由于它是父节点,因此它不需要ActorSelection,而可以只使用它的children 列表。看起来像这样:

    case object SpawnSession
    class SessionsManager extends Actor{
      context.system.scheduler.schedule(5 seconds, 5 seconds, self, GatherStats)(context.dispatcher)
      var sessionCount = 1
    
      def receive = {
        case SpawnSession =>
          val session = context.actorOf(Props[SessionActor], s"session-$sessionCount")
          println(s"Spawned session: ${session.path}")
          sessionCount += 1
          sender ! session
    
        case GatherStats =>
          println("Waking up to get session stats")
          context.children foreach (_ ! GetSessionStats)
    
        case SessionStats(f, b) =>
          println(s"got session stats from ${sender.path}, values are $f and $b")      
      }
    }
    

    并且可以进行如下测试:

    val system = ActorSystem("test")
    val manager = system.actorOf(Props[SessionsManager], "manager")
    manager ! SpawnSession
    manager ! SpawnSession
    Thread.sleep(10000)
    manager ! SpawnSession
    

    现在,这些示例非常琐碎,但希望它们能够为您描绘如何使用ActorSelection 或管理/监督动态解决此问题。还有一个好处是ask 既不需要也不需要阻塞。

    【讨论】:

    • 我认为选项 2 是更好的 IMO,因为它不依赖于演员的姓名(未键入)。让一个中心参与者产生所有会话参与者是一种更好的方法。唯一可能(至少直接)不起作用的情况是当您有多个参与者系统(即集群)时。但您也可以轻松扩展它以支持该用例。
    • 这取决于你想如何耦合你的会话。如果用户拥有会话并且用户 Actor 是会话的父级是有意义的,那么我会选择选项 1。如果非功能/交叉需要会话统计数据收集,它的类型安全性较低但也是非侵入性的消除顾虑。
    【解决方案2】:

    这个项目有很多额外的变化,所以我的答案/cmets已经延迟了很多:-/

    首先,会话统计数据收集不应该是定期的,而是应要求。我最初的想法是“误用”actor系统作为所有现有会话actor的映射,这样我就不需要主管actor知道所有会话。

    这个目标已被证明是难以实现的 - 会话参与者依赖于共享状态,因此会话创建者无论如何都必须观看会话。

    这使得选项 2 成为明显的答案 - 会话创建者无论如何都必须监视所有孩子。

    选项 1 最令人烦恼的障碍是“如何确定所有(当前)答案何时都存在” - 我希望统计请求对所有当前存在的参与者名称进行快照,查询它们,忽略失败(如果会话在可以查询之前就死了,在这里可以忽略)-统计请求只是一个调试工具,即类似于“尽力而为”的东西。 演员选择 api 让我陷入了一堆未来(我是 Scala/Akka 新手),所以我放弃了这条路线。

    因此选项 2 更适合我的需要。

    【讨论】:

      猜你喜欢
      • 2022-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      • 2011-07-26
      相关资源
      最近更新 更多