我的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。我注意到的不同之处在于演员不是根据需要自动创建/销毁的。
答案 0 :(得分:4)
根据您的目标,问题可能不是如何命名演员,而是何时创建演员。每次需要访问某些数据时,您都在创建一个新的actor。我想你不再需要那些老演员了。
你应该创建一个actor一次(或者如果你想要一个actor池,但是使用不同的名字,可以多次),然后通过在某个地方保持ActorRef
或使用dependency injected actors来重用它。如果您确实需要,还可以使用system.actorFor
或system.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
。