当前位置: 首页 > 知识库问答 >
问题:

获取或创建子Akka actor并确保生动性

白君之
2023-03-14

我试图使用Akka参与者的层次结构来处理每个用户的状态。有一个父角色拥有所有子角色,并以正确的方式处理get或create(参见a1、a2):

class UserActorRegistry extends Actor {
  override def Receive = {
    case msg@ DoPerUserWork(userId, _) =>
      val perUserActor = getOrCreateUserActor(userId)
      // perUserActor is live now, but will it receive "msg"?
      perUserActor.forward(msg)
  }

  def getOrCreateUserActor(userId: UserId): ActorRef = {
    val childName = userId.toActorName
    context.child(childName) match {
      case Some(child) => child
      case None => context.actorOf(Props(classOf[UserActor], userId), childName)
  }
}

为了回收内存,UserActors在空闲一段时间后过期(即计时器触发子actor调用context.stop(self))。

我的问题是,我认为“getOrCreateUserActor”和接收转发消息的子参与者之间存在竞争条件——如果子参与者在该窗口中过期,则转发消息将丢失。

是否有任何方法可以检测此边缘情况,或者重构UserActorRegistry以排除它?

共有1个答案

钱展
2023-03-14

我可以看出,您当前的设计存在两个问题,使您对您提到的比赛条件感到满意:

1) 具有终止条件(计时器发送毒药丸)直接转到儿童演员。通过采用这种方法,子线程当然可以在一个单独的线程(在调度程序中)上终止,同时,在UserActorRegistryactor(在调度程序中的另一个线程上)中设置了一条要转发给它的消息。

2)使用PoisonPill终止孩子。一个PoisonPill是一个优雅的停止,允许邮箱中的其他邮件首先被处理。在您的情况下,您是由于不活动而终止的,这似乎表明邮箱中没有其他邮件。我在这里看到一个PoisonPill是错误的,因为在您的情况下,另一条消息可能会在PosionPill之后发送,并且在处理了PoisonPill之后,该消息肯定会丢失。

因此,我将建议您将非活动子级的终止委托给UserActor注册表,而不是在子级中进行。当检测到不活动的条件时,向UserActor注册表的实例发送一条消息,指示需要终止特定的子节点。当您收到该消息时,通过Stop终止该子节点,而不是发送PoisonPill。通过使用以串行方式处理的UserActor注册表的单个邮箱,您可以帮助确保当您要向其发送消息时,子邮箱不会并行终止。

现在,这里有一个你必须处理的复杂问题。停止参与者是异步的。因此,如果您对孩子调用stop,则在处理DoPerUserWork消息时,它可能不会完全停止,因此可能会向其发送一条消息,该消息将丢失,因为它正在停止过程中。您可以通过保留一些内部状态(列表)来解决这个问题,这些状态表示正在被停止的子级。当您停止一个孩子时,将其名字添加到该列表中,然后在其上设置DeathWatch(通过context-watch-child)。当您收到该子项的Terminated事件时,请将其名称从要终止的子项列表中删除。如果您在孩子的名字在该列表中时收到了孩子的作品,请重新对其进行处理,最多可重复多次,以免永远尝试重新处理。

这不是一个完美的解决方案;它只是对你的方法中的一些问题的识别,并朝着正确的方向前进,以解决其中的一些问题。如果你想看这个的代码,请告诉我,我会把一些东西放在一起。

编辑

回应你的第二个评论。我不认为你能看到一个childActorRef并且看到它当前正在关闭,因此需要一个正在关闭的child列表。您可以增强DoPerUserWork消息,使其包含numberOfAttempts:Int字段,并将其递增,如果您看到目标子级当前正在关闭,则发送回self进行重新处理。然后,您可以使用numberOfAttempts来防止永远重新排队,在某个最大尝试次数处停止。如果你觉得依赖死亡之路不太舒服,你可以在关闭儿童的列表中添加一个生存时间组件。如果项目在列表中但在列表中的时间过长,则可以在遇到项目时对其进行修剪。

 类似资料:
  • 我可以使用创建演员,并使用查看他们。现在我想通过一些获得一个actor,如果它不存在,我想创建它。大概是这样的: 但是这不起作用,因为总是正确的,而且我得到

  • 问题内容: 我有一个以前被保留的实体,并且与另一个实体有关系。为了添加新实体,我只需将新实体添加到托管对象中,并用于保留更改。有没有一种方法可以获取新创建的对象的ID或获取我用于合并以更新其ID的原始(非托管)对象? 在伪代码中,我希望发生以下情况: 将为合并实体返回新副本 旧副本将针对新实体进行更新 示例:父级A,id = 13子级B,id = 0 从本质上讲,我想在父级上发布a ,但在子级上级

  • 问题内容: 我想编写一条Postgres SQL语句,该语句表示查找颜色X和亮度Y的用户。如果该用户存在,则返回其所有行数据。如果不是,请创建新行并传递其他信息。这两个单独的语句将执行以下操作: 如果那什么都不返回,那么执行以下命令: 有没有一种方法可以将这些组合成一个查询? 问题答案: 在SQL DBMS中,select-test- insert方法是一个错误:没有什么可以阻止另一个进程在您的和

  • 问题内容: 我正在尝试使用ActorFor获取现有的ActorRef,或者如果不存在则创建一个新的。我有以下代码,但它似乎没有按预期工作。.isTerminated()始终为true。 我在这里想念什么?提前致谢。 问题答案: “获取或创建”只能由指定的actor 的 父级 执行,因为只有该父级可以创建actor(如果不存在),并且只有父级才能一致地创建(即没有竞争条件)。在演员中,您可以做 不要

  • 我正在尝试使用ActorFor获取现有的ActorRef,或者如果它不存在,则创建一个新的ActorRef。我有以下代码,但它似乎没有按预期工作。isTerminated()总是正确的。 我错过了什么?提前谢谢。

  • 我目前用JavaFX制作了一个表单。我总是按一个按钮,我调用“addAnswer()”-方法。在其中,我创建了一个RadioButton、一个Label和一个deleteButton,我将它们捆绑在一个HBox中。我把所有的HBox都装在一个vBox中。现在的问题是删除按钮。我想深入研究点击按钮的HBox。 这是我的代码: 我在上面试过这个,但我意识到,所有的delAnswers-Button都有