管理Akka儿童演员地图的正确方法是什么?

管理Akka儿童演员地图的正确方法是什么?,akka,Akka,假设我有一个参与者,负责根据某个键将消息路由到子参与者集合,因此其内部状态如下所示: Map<String, ActorRef> children; 希望上面的代码足以让我大致了解我要做的事情。请注意,我使用map键作为子参与者的名称。问题在于,上述模式无法处理同一个键的添加、删除和添加消息的快速连续情况-它通常在第二次添加时失败,原因是: akka.actor.InvalidActorNameException: actor name [...] is not unique!

假设我有一个参与者,负责根据某个键将消息路由到子参与者集合,因此其内部状态如下所示:

Map<String, ActorRef> children;
希望上面的代码足以让我大致了解我要做的事情。请注意,我使用map键作为子参与者的名称。问题在于,上述模式无法处理同一个键的添加、删除和添加消息的快速连续情况-它通常在第二次添加时失败,原因是:

akka.actor.InvalidActorNameException: actor name [...] is not unique!
显然,在Remove messages上停止子参与者是异步的,这就是为什么它不起作用的原因,我正在努力寻找解决方案。我从中注意到以下几点,它准确地描述了我的问题:

虽然可以在以后创建一个具有相同路径的参与者[…],但这不是一个好的实践[…]

在非常具体的情况下,这可能是正确的做法,但一定要将处理这一问题的责任严格限制在演员的主管身上,因为这是唯一能够可靠地检测到名字被适当注销的演员,在此之前,新孩子的创建将失败

那么,在这里使用map键作为actor名称重用actor路径是正确的吗?如果是,我如何可靠地检测该名称的正确注销?如果不是,我应该为每个参与者的名字分配一个UUID吗?如果子项是持久参与者,这会导致问题吗?因为可以在前一个子项正确终止之前创建具有相同持久id的新子项。

您可以查看ActorRef的状态,以在参与者终止时获得通知。如果终止已发出,但您的主管尚未收到确认,则ActorRef被视为过时

如果为一个过时的演员添加了一个插件,那么您只需再次将该插件发送给自己,希望最终终止:

HashSet<String> staleActorRefs = new HashSet<String>();

if(message instanceOf Add) {
    if(staleActorRefs.contains(message.getKey())) {
      getSelf().forward(message, getContext()); 
    } else {
      children.put(message.getKey(), getContext().watch(getContext().actorOf(childProps, message.getKey())));
    }
} else if (message instanceOf Remove) {
    getContext().stop(children.get(message.getKey());
    staleActorRefs.add(message.getKey());
    children.remove(message.getKey());
} else if (message instanceOf RouteToChild) {
    children.get(message.getKey()).forward(message, getContext());
} else if (message instanceOf Terminated) {
    staleActorRefs.remove(message.actor().path().name());
}
这种递归消息意味着您的主管将不断尝试添加,直到终止完成

如果在ActorRef过时时RouteToChild到达,则会出现问题,但该情况的解决方案是开放式的,并且未在问题中指定

不要这样做

话虽如此,我同意问题中的引述:这不是好做法

使用UUID,省去你的头痛

HashSet<String> staleActorRefs = new HashSet<String>();

if(message instanceOf Add) {
    if(staleActorRefs.contains(message.getKey())) {
      getSelf().forward(message, getContext()); 
    } else {
      children.put(message.getKey(), getContext().watch(getContext().actorOf(childProps, message.getKey())));
    }
} else if (message instanceOf Remove) {
    getContext().stop(children.get(message.getKey());
    staleActorRefs.add(message.getKey());
    children.remove(message.getKey());
} else if (message instanceOf RouteToChild) {
    children.get(message.getKey()).forward(message, getContext());
} else if (message instanceOf Terminated) {
    staleActorRefs.remove(message.actor().path().name());
}