在Akka Typed中向父Actor发送消息

4

标题已经很明确了,我想要能够向父级actor发送消息(也就是说,我想要父级的ActorRef)。在Akka Classic(未类型化)中,可以通过子级的ActorContext获取父级actor的ActorRef

context.parent

(例如,请参见此Java问题。)

然而,Akka Typed中的akka.actor.typed.scaladsl.ActorContext没有为父级暴露ActorRef。在Scala中,有没有惯用的方法来获取父actor的ActorRef


1
投票重新开放,因为涉及的API自被标记为重复问题以来已经发生了巨大变化。Akka 2.6与之前版本有很大不同,覆盖先前版本和2.6的答案可能会显得笨重。 - Levi Ramsey
另外,OP的问题是在Scala中提出的,而建议的重复问题是Java。在Akka 2.6中,特别是在类型化API中,一般建议根据语言使用javadslscaladsl:因此,在Java中做某事和在Scala中做某事的最佳答案可能会有所不同(尽管相似)。 - Levi Ramsey
1
用户的原始帖子提到“许多过时的答案”不起作用。如果不使用类型化API,则这些过时的答案可以正常工作。(请注意,如果从官方Akka文档中的Akka Actor链接跟随,将会进入类型化API的文档https://doc.akka.io/docs/akka/current/typed/index.html,即如果是新手,则类型化API是akka-actor) - Levi Ramsey
1
我还会提供一个适用于Java的答案,涉及到目标问题中的经典和类型化内容,因为这可能更具有重要性。 - Levi Ramsey
显示剩余6条评论
2个回答

4

简述:

在创建子Actor时将父Actor引用注入到子Actor中。

Akka Typed 强制实施严格的协议,因此您需要明确表明“此 Actor 与另一个 Actor 通信”。接受的答案是一种解决方法(将其转换为经典类型并使用父级),但它也有其缺点:现在您无法再强制执行类型。

以下是一些可以帮助您入门的代码。请注意所有类型都得到了强制执行。您可以以不同方式建模特征,但应该了解基本思路:

object ParentActor {
  sealed trait Command 
  
  case class DoSomething() extends Command
  
  // you do not have to do this, but creating another trait
  // allows you to narrow the amount of messages the parent can receive from the child
  sealed trait ChildNotification extends Command
  case class MessageFromChild() extends ChildNotification

  
  def apply(): Behavior[Command] = {
    Behaviors.receive( (context, message) => 
      message match {
        case DoSomething() =>
          // create a child that knows about its parent
          context.spawn(ChildActor(context.self), "child")
          Behaviors.same

        case MessageFromChild() =>
          context.log.info("I received a message from my child")
          Behaviors.same
      })
  }
}

object ChildActor {
  sealed trait Command
  case class Work() extends Command
  
  // inject the parent here (or any other actor that matches the signature)
  def apply(parent: ActorRef[ParentActor.ChildNotification]): Behavior[Command] = {
     Behaviors.receive( (context, message) => 
       message match {
         case Work() =>
           // send message to parent actor (or any other actor with that type)
           parent ! ParentActor.MessageFromChild()
           Behaviors.same

     })
  }
}

顺便提一下,我正在使用akka typed的"函数式"语法,但你也可以使用更"面向对象"的语法。 它遵循相同的方法。


3

如果您使用的是Typed Akka,在所有可能的父级ActorRef中涵盖的[Scala]类型是ActorRef[Nothing],它是一个无法发送消息的ActorRef,所以它的实用性有限。

至少在经典API存在的时候如此:

import akka.actor.typed.scaladsl.adapter._

type ClassicActorRef = akka.actor.ActorRef

val parentActorRef = context.toClassic.parent

这将是一个未类型化的ActorRef,也就是说,您可以自由地发送父级actor永远不会接受的消息。

如果您想要一个actor父级的类型引用,您需要在生成子actor时嵌入它,就像如果您想要当前消息发送方的类型引用,您需要在协议中嵌入replyTo一样。

(对于同样的原因,已类型化的ActorContext中缺少context.sender,就像缺少context.parent一样; 复制经典的context.sender的解决方法是类似的:context.toClassic.sender


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