我试图使用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) 具有终止条件(计时器发送毒药丸)直接转到儿童演员。通过采用这种方法,子线程当然可以在一个单独的线程(在调度程序中)上终止,同时,在UserActorRegistry
actor(在调度程序中的另一个线程上)中设置了一条要转发给它的消息。
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都有