如何仅使用actorSystem从一组actor中收集状态信息?

时间:2014-12-01 12:14:57

标签: scala akka

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

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

在与Scala的类型系统进行死亡比赛后,我不得不放弃。 真的没办法做这样的事吗?

2 个答案:

答案 0 :(得分:3)

如果我正确地理解了这个问题,那么我可以提供几种方法来实现这一目标(尽管肯定还有其他方法)。

选项1

在这种方法中,将有一个演员负责定期醒来并向所有会话演员发送请求以获取他们当前的统计数据。该actor将使用带有通配符的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")

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

选项2

有点类似的方法,但在这一方面,我们使用集中式演员来产生会话演员并充当他们的主管。这个中心角色还包含定期轮询统计数据的逻辑,但由于它是父级,因此它不需要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,也没有阻止。

答案 1 :(得分:1)

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

首先,会话统计信息收集不应该是定期的,而是应要求提供的。我最初的想法是“误用”演员系统作为我所有现有会话演员的地图,这样我就不需要知道所有会话的主管演员了。

此目标显示难以捉摸 - 会话参与者依赖于共享状态,因此会话创建者必须始终观看会话。

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

选项1最令人烦恼的障碍是“如何确定何时所有(当前)答案都存在” - 我希望统计信息请求拍摄所有当前存在的演员名称的快照,查询它们,忽略失败(如果会话)在可以查询之前死掉,这里可以忽略它) - 统计请求只是一个调试工具,就像“尽力而为”。 演员选择api纠缠在一个未来的丛林中(我是Scala / Akka新手),所以我放弃了这条路线。

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