播放 2.4 使用 Guice 创建一个 actor 来处理 websocket

Play 2.4 create an actor for handle websocket with Guice

首先感谢大家花时间审阅我的问题。我是 scala 生态系统的新手,所以我觉得我混淆了一些概念。

我在 Play 2.4 项目中引入 Guice,它在一些 REST 控制器中工作。我编辑了 build.sbt 以附加 routesGenerator := InjectedRoutesGenerator 作为 Play 官方文档的推荐,并编辑了我的路线,如您在以下示例中所见:

GET        /socket       @com.letgo.chat.controllers.ChatController.socket

我用于注入演员的模块如下所示:

class ChatModule extends AbstractModule with ScalaModule with AkkaGuiceSupport with GuiceAkkaActorRefProvider {
  override def configure() {

    bindActor[TalkerProviderActor](TalkerProviderActor.name)
    ...

所有这些似乎都有效。

但是另一个端点使用 Play 方法处理 websocket WebSocket.acceptWithActor。我需要创建一个也需要一些依赖项的演员。

控制器创建 ConnectionActor:

class ConnectionActor(
  @Assisted() websocket: ActorRef,
  monitoring: Monitoring,
  @Named(BouncerActor.name) bouncer: ActorRef,
) extends Actor

...

class ChatController @Inject() extends Controller {
  def socket(): WebSocket[String, JsValue] = WebSocket.acceptWithActor[String, JsValue] { request => out =>
    // I had the following statement in order to build the actor before introducing Guice:
    // ConnectionActor.props()
    // Now, I need some magic here
  }
}

正如你所看到的,我需要一个 websocket: ActorRef 作为 websocket 输出。

我使用 AkkaGuiceSupport 提供的 bindActorFactory 方法在应用程序的其他部分创建了一些参与者:

bindActorFactory[TalkerActor, TalkerActor.Factory]

但我不知道在这种情况下我应该如何创建一个 actor 来处理 websocket。你们能帮帮我吗?

谢谢

我相信这可以解决问题:

package controllers

import javax.inject._

import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.stream.Materializer
import play.api.libs.streams.ActorFlow
import play.api.libs.ws.WSClient
import play.api.mvc._

import scala.concurrent.ExecutionContext.Implicits.global

@Singleton
class SocketActorProvider @Inject() (ws: WSClient) {
  def get(out: ActorRef) = Props(new SocketActor(out, ws))
}

class SocketActor(out: ActorRef, ws: WSClient) extends Actor {
  override def receive: Receive = {
    case "ping" => ws.url("http://localhost:9000/pong").get().foreach(pong => out ! pong.body)
  }
}

@Singleton
class HomeController @Inject() (implicit system: ActorSystem, ws: WSClient, materializer: Materializer, provider: SocketActorProvider) extends Controller {

  // route '/ws'
  def socket = WebSocket.accept[String, String] { request =>
    ActorFlow.actorRef(out => provider.get(out))
  }

  // route '/pong'
  def pong = Action {
    Ok("PONG!")
  }

  // route '/'
  def index = Action {
    Ok("""
      <script>
        var ws = new WebSocket('ws://localhost:9000/ws');
        ws.onopen = () => console.log('I am open!');
        ws.onmessage = m => console.log(m.data);

        setInterval(() => {console.log('ping'); ws.send('ping')}, 1000)
      </script>
    """).as("text/html")
  }

}

如您所见,Guice 注入了一个提供程序,其中注入了 WSClient,并且在构建 actor 时它传入了依赖关系