如何命名演员?

时间:2016-11-23 02:13:04

标签: scala playframework akka playframework-2.5

我的Web应用程序中的数据层由Akka actor组成。每当我需要访问数据时,我都会调用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)

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

  

play.api.http.HttpErrorHandlerExceptions $$ anon $ 1:执行   exception [[InvalidActorNameException:actor name [myactor]不是   独特!]]

** 编辑 **

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

2 个答案:

答案 0 :(得分:4)

根据您的目标,问题可能不是如何命名演员,而是何时创建演员。每次需要访问某些数据时,您都在创建一个新的actor。我想你不再需要那些老演员了。

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

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

如果你每次都必须创建一个单独的演员,那么请看Wonpyo的答案。但是,在我看来,您可以直接使用Future代替。

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

编辑:

既然你指定了你希望每个actor都像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}")
}

或者,您可以使用personDao(或在Akka 2.4中使用system.actorFor("personDao")等效字段代替system.actorSelection。您还可以inject actors directly

如果您希望多个演员并行处理您的消息,可以使用routers。例如:

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

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

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

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

答案 1 :(得分:1)

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不会被破坏

     

(演员未被GC终止)

如果你一直在调用这个函数,那么你的记忆将在某一天空间不足。

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