使用Akka.NET的Actor模型:如何防止向已死亡的Actor发送消息

6
我正在使用Akka.NET实现一个演员系统,在该系统中,一些演员是按需创建的,并在可配置的空闲期后删除(我使用Akka的“ReceiveTimeout”机制来实现)。每个演员都由一个键标识,并且不应存在两个具有相同键的演员。
这些演员目前由一个常规监督者创建和删除。监督者可以被要求返回与给定键匹配的演员的引用,无论是返回现有的还是创建新的,如果不存在具有此键的演员。当演员接收到“ReceiveTimeout”消息时,它会通知监督者,监督者再使用“PoisonPill”杀死它。
当我在演员被删除后立即向其中一个演员发送消息时,出现了问题。我注意到向已死亡的演员发送消息不会生成异常。更糟糕的是,当发送“Ask”消息时,发送方仍然被阻塞,无限期地等待响应,但永远不会收到响应。
我最初考虑使用Akka的“Deatchwatch”机制来监视演员的生命周期。但是,如果我没有弄错,垂死的演员发送的“Terminated”消息将像任何其他消息一样异步地被监视演员接收,因此在目标演员死亡和接收其“Terminated”消息之间仍可能出现问题。
为了解决这个问题,我让任何向监督者请求这样一个演员引用的人都必须向监督者发送一个“关闭会话”消息,在他不再需要它时释放演员(这是由一个可处理的“ActorSession”对象透明地完成的)。只要演员有任何开放的会话,监督者就不会删除它。
我想这种情况很常见,因此想知道是否有更简单的模式可以遵循来解决这种问题。任何建议都将不胜感激。
2个回答

3
我在删除一个actor后立即向其发送消息时会遇到问题。我注意到向已经死亡的actor发送消息不会生成异常。
这是有意设计的。你不会在尝试发送消息时收到异常,它只会被路由到Deadletters并进行记录。这有很多原因我这里不再赘述,但最终结果是这是预期行为。
DeathWatch是解决此问题的正确工具,但正如您所指出的那样,您可能会在向该actor发送消息之后接收到Terminated消息。
比跟踪开放/关闭会话更简单的模式是仅使用来自接收方的确认/回复消息,使用Ask + Wait +硬超时。当然,缺点是如果您的接收方actor有许多长时间运行的操作,则您可能会在sender内部阻塞很长时间。
您可以选择的另一种选项是重新设计您的接收方actor以充当状态机,并使用软终止或正在终止的状态来耗尽与潜在发送者的连接/引用。这样,原始actor仍然可以回复和接受消息,但让调用者知道它不再可用于执行工作。

感谢您的回答,Aaron。 - Odsh
我考虑了使用Ask + timeout的解决方案,但我不喜欢我的演员因为等待超时而无所事事的想法。我意识到由于Akka中的“最多一次”交付可靠性,这迟早会发生,但至少在那种情况下,这将是演员之间的消息传输问题,而不是设计约束。我喜欢“终止”状态的想法,我会再仔细考虑一下。然而,演员仍然不知道它将在何时不再接收任何消息,因此也不知道何时可以删除自身。 - Odsh
@Odsh “然而,该演员仍然不知道在什么时候它将不再接收任何消息,从而可以删除自身。” - 如果接收消息的演员不知道何时停止接收消息,发送消息的演员也不知道何时停止发送消息,那么这是你的设计问题。其中一个演员必须能够做出决定或向其他能够做出决定的演员报告。 - Aaronontheweb
Aaron,发送和接收的角色知道他们将停止交换消息的时间。然而,接收方角色不知道何时新的角色会想要与其进行通信。 - Odsh
1
很好的回答,但我想补充一点,你也可以使用Actorselection来检查Actor: "你也可以使用ActorSelection的resolveOne方法获取ActorRef。如果存在这样的Actor,则它将返回匹配的ActorRef的Future。如果没有这样的Actor存在或者识别在提供的超时时间内未完成,则它将以失败的形式完成,即akka.actor.ActorNotFound。" - Chanan Braunstein

0

我通过使用Akka的Cluster Sharding机制创建实体actor来解决了这个问题:

如果实体的状态是持久化的,您可以停止未使用的实体以减少内存消耗。这可以通过应用程序特定实现实体 actor 来完成,例如通过定义接收超时(context.setReceiveTimeout)。如果在实体停止自身时,已经将消息排队到实体,则邮箱中的已排队消息将被丢弃。为了支持优雅的去活动而不丢失这些消息,实体 actor 可以向其父 Shard 发送ShardRegion.Passivate。Passivate 中指定的包装消息将发送回实体,然后实体将停止自身。在接收 Passivate 和实体终止之间,Shard 缓冲传入消息。这些缓冲的消息随后将传递给实体的新版本。


你需要实现 Cluster Sharding 机制的所有方面(全文逐一查看),还是只部分实现以解决此问题? - Minh Kha
那是很久以前的事了,Minh Kha,我不记得具体细节了。如果我的记忆没有出错,那就是我使用集群分片的唯一原因,所以我尽量保持最少化。 - Odsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接