【问题标题】:Play Framework, Akka, Guice : How to create multiple instances of an actor (which has dependencies) with Guice inside controller?Play Framework、Akka、Guice:如何在控制器中使用 Guice 创建多个参与者实例(具有依赖项)?
【发布时间】:2025-05-28 15:35:01
【问题描述】:

play framework doc (https://www.playframework.com/documentation/2.6.x/ScalaAkka) 中有一个例子,解释了如何将actor(需要注入的依赖项)注入控制器:

@Singleton
class Application @Inject() 
(@Named("configured-actor") configuredActor: ActorRef)
(implicit ec: ExecutionContext) extends Controller {

      implicit val timeout: Timeout = 5.seconds

      def getConfig = Action.async {
        (configuredActor ? GetConfig).mapTo[String].map { message =>
          Ok(message)
        }
      }
    }

但据我了解,它创建了一个演员的单个实例(例如,单例)。

我需要在控制器内创建多个configuredActor 实例。

还有一个示例演示了如何从父actor创建子actor(需要注入依赖项)实例

object ParentActor {
  case class GetChild(key: String)
}

class ParentActor @Inject() (
    childFactory: ConfiguredChildActor.Factory
) extends Actor with InjectedActorSupport {
  import ParentActor._

  def receive = {
    case GetChild(key: String) =>
      val child: ActorRef = injectedChild(childFactory(key), key)
      sender() ! child
  }
}

我尝试在控制器中应用这种技术,但 injectedChild(childFactory(key), key) 需要(隐式)actor 上下文

def injectedChild(create: => Actor, name: String, props: Props => Props = identity)(implicit context: ActorContext): ActorRef = ...

但我需要从 ActorSysem 创建这个演员(例如 /user)。

(我想得到/user actor 的上下文,但是怎么做?对吗?)

使用 Guice 在父 Actor 之外(例如,在控制器内部)创建多个 Actor 实例的正确方法是什么?

【问题讨论】:

    标签: dependency-injection playframework-2.0 akka guice


    【解决方案1】:

    我认为没有办法从演员外部获取 ActorContext,但如果你想从 /user 演员创建子演员,你可以直接system.actorOf

    问题就像你说的那样,InjectedActorSupport 需要一个 ActorContext。 但是如果你看这个特征,它并没有什么花哨的东西,只是

      def injectedChild(create: => Actor, name: String, props: Props => Props = identity)(implicit context: ActorContext): ActorRef = {
        context.actorOf(props(Props(create)), name)
      }
    

    因此,解决方案是扩展 trait 并添加一个接收 ActorSystem 而不是 ActorContext 的方法。

      def injectedChild2(create: => Actor, name: String, props: Props => Props = identity)(implicit system: ActorSystem): ActorRef = {
        system.actorOf(props(Props(create)), name)
      }
    

    事实上,你甚至可以直接将方法添加到控制器中,但如果你想在多个控制器中这样做并不方便。

    更新:

    虽然这在技术上是正确的,但我认为(正确地)缺少接收 ActorSystem 的方法是有意的。

    所以,我已经 asked to the play dev team 和 James Roper 回答了:

    这样做的原因是直接从演员系统外部创建许多演员通常不是一个好习惯,因为你不能监督他们 - 也就是说,你不能说应该做什么当它们崩溃时发生(应该停止、重新启动、升级等?)。在我想从控制器创建角色的所有场景中,对于我来说,不这样做总是更好的设计,而是向另一个经理角色发送消息以创建子角色。当你这样做时,它在未来为你提供了更多的灵活性,例如,假设将来你有一个要求,当用户重新提交相同的操作时,它不应该启动一个新的 Actor但应该连接到现有的演员。如果你有一个经理 Actor,这很容易做到,你可以自动检查经理 Actor 是否已经有一个用于该操作的子节点,并在必要时创建一个新子节点。如果没有,那是不可能的。然后在未来你转移到一个多节点设置,你可能决定在一个集群中对你的actor进行分片 - 同样,非常容易如果你使用的是管理器actor,因为你需要做的就是将管理器actor替换为集群分片,或者在你的管理器前面放置一个集群一致的哈希路由器,但如果直接从控制器创建参与者,则不可能

    【讨论】: