3

我正在尝试在我的 Play 2.5 应用程序中使用 Akka 演员和依赖注入。我基本上遵循了那个文档,这里是我的代码的一瞥:

演员

基本上,我有一个简单的参与者,它接收一些数据,处理它,然后将其发送回调用者。

package actors    

import akka.actor._
import akka.pattern.{AskTimeoutException, ask}
import akka.util.Timeout
import javax.inject._

import com.google.inject.assistedinject.Assisted
import play.api.Configuration

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future

object ServiceActor {
  val ConfigKey = "actors.path" // remote actor path

  trait Factory {
    def apply(key: String): Actor
  }
}

class ServiceActor @Inject()(configuration: Configuration,
                                   actorSystem: ActorSystem,
                                   @Assisted key: String)
                                  (implicit val ec: ExecutionContext)
  extends Actor {
  import ServiceActor._

  implicit val timeout = Timeout(5.minutes)

  def receive = {
    case data: SomeData => sender ! someFunction(data)
  }

  // implemented in the real code, omitted because not important for the question
  def someFunction = ??? 
}

模块

根据文档,我需要制作一个简单的模块并激活它。这是模块:

package modules

import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport

import actors.ServiceActor

class ServiceModule extends AbstractModule with AkkaGuiceSupport {
  def configure = {
    bindActor[ServiceActor]("service-actor")
  }
}

然后我通过将以下行添加到application.conf

play.modules {
  enabled += modules.ServiceModule
}

控制器

最后,我尝试在控制器中使用演员:

package controllers

import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import javax.inject._

import play.api.mvc._

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext

@Singleton
class SearchController @Inject()(@Named("service-actor") serviceActor: ActorRef)
                                (implicit ec: ExecutionContext)
  extends Controller {

  implicit val timeout = Timeout(5.minutes)

  def search(param: String) = Action.async {

    val request = SomeRequestType(param)

    (serviceActor ? request).mapTo[SomeResponseType] map { result =>
      Ok(result.toString)
    }
  }
}

据我所知,我已经完美地遵循了文档,但是调用控制器给了我这个错误:

[AskTimeoutException: Recipient[Actor[akka://application/user/service-actor#-366470383]] 
had already been terminated. 
Sender[null] sent the message of type "some.namespace.SomeRequestType".]

据我了解,错误是关于演员是 a 的null,我们无法向它发送消息。虽然错误代码说“已经被终止”,但看起来演员从来没有被启动过。

我不确定首先要看哪里,我做错了什么,因为我没有使用 Guice 的经验。

问题是,我做错了什么?为什么actor没有启动,或者如果它确实已经终止,为什么它会被终止?我该如何解决?

非常感谢。

4

1 回答 1

0

希望你已经解决了这个问题。但是,我仍然在这里发布答案只是为了一些新来者的参考,因为我花了一段时间才弄清楚这个问题的原因。

据我所知,错误是关于演员如何为空,我们无法向它发送消息。

不完全是。问图案?有一个sender的隐式参数,默认是ActorRef.noSender,源码在这里

通常,如果您在 Actor 内部,您在范围内有一个名为 self 的隐式 ActorRef,但由于您不在 Actor 中,它只是采用默认值。

虽然错误代码说“已经被终止”,但看起来演员从来没有被启动过。

正确的。很可能您的演员一开始就没有正确启动。在您的控制台中,您应该会看到比 Play Webpage 响应中看到的更多错误消息。

例如,下面是我在收到 AskTimeoutException 之前实际遇到的错误。

1 error
akka.actor.ActorInitializationException: akka://application/user/user-actor: exception during creation
    at akka.actor.ActorInitializationException$.apply(Actor.scala:193)
    at akka.actor.ActorCell.create(ActorCell.scala:608)
    at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:462)
    at akka.actor.ActorCell.systemInvoke(ActorCell.scala:484)
    at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:282)
    at akka.dispatch.Mailbox.run(Mailbox.scala:223)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
    at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
Caused by: com.google.inject.ConfigurationException: Guice configuration errors:

最后,您可能需要再次检查 sbt 控制台以及您的注入,以确保首先启动了 actor。

于 2017-12-21T03:33:42.917 回答