如何通过Play中的Web套接字向actor发送消息!框架?

时间:2018-05-11 23:11:19

标签: scala playframework websocket akka actor

这是一个基本问题,但谷歌搜索几个小时后我找不到令人满意的答案。从here中的示例来看,制作Web套接字的方法是这样的:

控制器代码:

import play.api.mvc._
import play.api.libs.streams.ActorFlow
import javax.inject.Inject
import akka.actor.ActorSystem
import akka.stream.Materializer

class Application @Inject()(cc:ControllerComponents) (implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {

  def socket = WebSocket.accept[String, String] { request =>
    ActorFlow.actorRef { out =>
      MyWebSocketActor.props(out)
    }
  }
}

演员代码:

import akka.actor._

object MyWebSocketActor {
  def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}

class MyWebSocketActor(out: ActorRef) extends Actor {
  def receive = {
    case msg: String =>
      out ! ("I received your message: " + msg)
  }
}

但是我究竟如何通过网络套接字从控制器向演员发送消息?让我们在控制器代码中说,我有一个操作代码,当按下按钮时它会处理,它会向actor发送一个字符串块。如何从控制器代码将此字符串发送到上面的actor?

3 个答案:

答案 0 :(得分:0)

首先让我们了解已经创建的内容以及需要添加的内容。 =IF(AND(I5<0,05;0,05);IF(AND(I5>0,05;I5);IF(AND(I5>0,4;0,4);IF(AND(I5<0,4;I5);"")))) 的类型是WebSocket

socket揭示了一个WebSocket方法:

apply

因此,只要您没有发送消息,就尚未创建流。现在,一旦发送了一条消息,我们就可以创建流程并发送一条消息:

def apply(request: RequestHeader): Future[Either[Result, Flow[Message, Message, _]]]

答案 1 :(得分:0)

sample apprelated discussion可能会有所帮助。以下是从链接的示例应用程序中裁剪/总结的一些代码:

Flow.futureFlow(futureUserActor.map { userActor =>
  val incomingMessages: Sink[Message, NotUsed] =
    Flow[Message]
      .map(...)
      .to(...)

  val outgoingMessages: Source[Message, NotUsed] =
    ActorSource
      .actorRef[User.OutgoingMessage](...)
      .mapMaterializedValue { outActor =>
        // give the user actor a way to send messages out
        userActor ! User.Connected(outActor)
        NotUsed
      }
      .map(...)

  // then combine both to a flow
  Flow.fromSinkAndSourceCoupled(incomingMessages, outgoingMessages)
})

答案 2 :(得分:0)

至少有两种方法可以解决此问题:

  1. 自定义播放的ActorFlow.actorRef方法以返回基础参与者。之前有一个similar discussion,这里是gist。如果将基础参与者放入(用户,websocket)映射中,请确保使用诸如TrieMap之类的线程安全实现。
  2. 可以通过创建事件总线并从actor内订阅事件总线来解决您要尝试执行的操作。然后,您可以过滤您感兴趣的事件并做出相应的反应。此解决方案更好,因为它实际上可以扩展-您可以具有多个Web应用程序副本(在这种情况下,第一种方法将不起作用,因为不包含对用户WS actor的引用的副本可以接收按钮点击事件)。用伪代码来说明这个想法:
sealed trait AppEvent
final case class ButtonClicked(user: User.ID) extends AppEvent

// inside an action
system.eventStream.publish(ButtonClicked(request.identity.id))

// inside your actor
override def preStart =
  context.system.eventStream.subscribe(self, classOf[AppEvent])

请注意,事件总线的概念是抽象的。我上面展示的是使用akka的classic event bus(在本地工作)的最基本方法。对于这种扩展方法,您需要在幕后有一个实际的消息队列。