【问题标题】:How to name an actor?如何给演员起名字?
【发布时间】:2016-11-23 02:13:04
【问题描述】:

我的 Web 应用程序中的数据层由 Akka 演员组成。每当我需要访问数据时,我都会像这样调用 ActorSystem 机制:

val myActor = system.actorOf(Props[MyActor], name = "myactor")      
implicit val timeout = Timeout(120 seconds)
val future = myActor ? Request1
val result = Await.result(future, timeout.duration)

我用的是Play,ActorSystem变量是通过注入获得的:

class MyClass @Inject() (system: ActorSystem)

但是我收到以下异常,说我访问该函数时演员名称不是唯一的第二次,如何解决这个问题?考虑到可以被多个线程同时使用,如何给actor命名?

play.api.http.HttpErrorHandlerExceptions$$anon$1:执行 异常[[InvalidActorNameException:演员名称[myactor]不是 独一无二!]]

** 编辑 **

我想要实现的目标类似于在 EJB 模型中拥有一个实体 Bean 容器,其中每个参与者都是一个实体 Bean。我注意到的不同之处在于,actor 不会根据需要自动创建/销毁。

【问题讨论】:

  • 所以你想为每个实体/文档/行单独的Actor?在这种情况下,我会在行的键之后命名它们。您应该创建一个代表您的实体集合(行表)的“父”actor,然后查询该“父”actor 以获取特定键。如果已经有这个key对应的actor,则返回,如果没有,先创建。
  • 每个actor都应该像一个DAO类,有create/read/update/delete方法
  • 好的,这是有道理的。我会更新我的答案。

标签: scala playframework akka playframework-2.5


【解决方案1】:

根据您的目标,问题可能不是如何命名演员,而是何时创建演员。每次您需要访问某些数据时,您都在创建一个新参与者。我想你不会在不再需要老演员时阻止他们。

你可能应该创建一个actor一次(或者如果你想要一个actor池,但使用不同的名称,可以创建多次)并在以后通过在某处保留ActorRef或使用dependency injected actors来重用它。如果您确实需要,也可以使用system.actorForsystem.actorSelection(取决于您使用的 Akka 版本)。

大多数情况下,您甚至不需要明确的ActorRef,因为您想reply to a sender 一些消息。

如果您每次都必须创建一个单独的演员,那么请参阅元杓的回答。不过,在我看来,您可以直接使用Future

有一个很棒的guide on Actors in the Akka documentation

编辑:

由于您指定希望每个参与者都像 DAO 类一样行事,我认为它应该看起来像:

// Somewhere in some singleton object (injected as dependency)
val personDao : ActorRef = system.actorOf(Props[PersonDaoActor], name = "personDao")
val fruitDao  : ActorRef = system.actorOf(Props[FruitDaoActor],  name = "fruitDao")

然后,当你需要访问一些数据时:

val johnSmithFuture = personDao ? Get("John Smith")
johnSmithFuture.map {
  case Person(name, age) => println(s"${name} ${age}")
}

或者,您可以使用system.actorFor("personDao")(或Akka 2.4 中的system.actorSelection 等效项)代替personDao。你也可以inject actors directly

如果您希望多个参与者并行处理您的消息,您可以使用routers。示例:

val personDao: ActorRef =
  system.actorOf(RoundRobinPool(5).props(Props[PersonDaoActor]), "personDao")

它将创建您的 PersonDaoActor 的 5 个实例,并将发送到 personDao 的任何消息分发给这 5 个参与者,因此您可以并行处理 5 个查询。如果所有 5 个演员都忙,则消息将排队。

在这种情况下,使用 Await 违背了 Akka 的目的。在某些情况下,这是唯一的选择(主要是遗留代码),但每次都有效地使用它会使您的代码完全阻塞,甚至可能是单线程的(取决于您的参与者代码)。在 Play 中尤其如此,它旨在异步执行所有操作,因此无需 Await

重新考虑演员是否真的是您问题的最佳解决方案可能是个好主意。如果您想要的只是并行执行,那么Futures 会简单得多。在这种情况下,有些人仍然使用演员,因为他们喜欢路由的抽象性和简单性。我发现一篇有趣的文章详细描述了这一点:"Don't use Actors for concurrency"(另请阅读 cmets 以了解反对意见)。

【讨论】:

  • 我不必每次都创建单独的演员,请看我的编辑
  • 如果我在某个单例对象中声明演员,如果两个类访问同一个演员声明会发生什么?他们会并行访问两个不同的参与者吗?
  • 一般来说,不,ActorRef 指向单个参与者实例,因此对参与者的请求将被排队。有一些例外,请参阅dispatchers。如果你想创建一个演员池,你可以使用routers。例如,根据您的路由器,它们可以以循环方式处理消息。我认为一开始考虑演员的最简单方法是将它们视为具有某种内部状态的消息队列。一个演员 = 一个队列。
  • @ps0604 请看我更新的答案,希望现在更清楚了。
  • 感谢您的好回答。似乎使用routes 会分发/平衡消息,而我不会使用Futures。另外,使用routes,我可以预先指定要创建的演员数量。
【解决方案2】:

Actor System 要求每个 Actor 的名称(路径)唯一。

路径格式如下akka://system@host:port/user/{your-actor-path}

例如

   val system = ActorSystem("hello")
   val myActor = system.actorOf(Props[MyActor], name ="myactor")
   //  myActor Path 
   // "akka://hello/user/myactor"  // purely local
   // "akka.tcp://hello@ip:port/user/myactor" // remote

在您的代码中,每次调用都会创建 myActor。

这使得演员每次都在同一条路径上。

因此,不好的解决办法是改代码如下

 val myActor = system.actorOf(Props[MyActor])      

如果你不给演员分配名字,那么演员系统会随机分配一个名字

myActor 对于每个函数调用不会有相同的路径。

但是,这确实是个糟糕的解决方案,因为myActor 不会被破坏

(Actor 没有被 GC 终止)

如果你一直调用这个函数,那么你的内存总有一天会用完的。

所以,请在您完成该功能后DESTRUCT myActor

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-22
    • 2014-01-20
    • 2014-07-11
    • 2018-12-10
    • 2015-10-01
    • 2020-08-23
    • 2016-09-04
    • 2015-02-28
    相关资源
    最近更新 更多