Это довольно простой вопрос, но я не смог найти удовлетворительного ответа после нескольких часов поиска в Google. Из примера в здесь способ создания веб-сокета выглядит примерно так:
Код контроллера:
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)
}
}
Но как именно отправить сообщение от контроллера актеру через веб-сокет? Скажем, в коде контроллера у меня есть код действия, который обрабатывает, когда кнопка нажата, он отправляет блок строки актеру. Как отправить эту строку указанному выше актеру из кода контроллера?
@ Питер, я не мог решить эту проблему. В итоге я просто использовал флягу с java-скриптом.





Я могу предоставить вам несколько примеров веб-сокетов в Play. По сути, они используют Flow (akka-streams) для обработки соединения через веб-сокет.
Есть официальный пример Play Websocket: Пример Lightbend Websocket
Исходя из этого, у меня есть несколько проектов, использующих веб-сокеты, например:
Это пример приложения, показывающий, как вы можете интегрировать проект Play с проектом Scala.js, Binding.scala - используя веб-сокеты.
Это довольно сложно, поэтому самый простой способ - проверить HomeController, UserParentActor, UserActor и АдаптерАктёр, как они работают вместе.
Это фреймворк, основанный на приведенном выше примере, который также показывает, как регистрировать клиентов websocket.
Давайте сначала разберемся, что уже создано, а что нам нужно добавить. Тип socket - это WebSocket.
Этот WebSocket раскрывает единственный метод apply:
def apply(request: RequestHeader): Future[Either[Result, Flow[Message, Message, _]]]
Следовательно, пока вы не отправили сообщение, поток еще не создан. Теперь, когда сообщение отправлено, мы можем создать поток и отправить ему сообщение:
def index = Action.async(parse.json) { request =>
socket(request).map {
case Left(result) =>
Ok("Done: Left: " + result.body)
case Right(value) =>
Source.single(TextMessage(Json.stringify(request.body))).via(value).to(Sink.ignore).run()
Ok("Done: Right: ")
}
}
Эти образец приложения и связанное обсуждение могут быть полезны. Вот фрагмент кода, вырезанный / обобщенный из связанного примера приложения:
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)
})
Есть как минимум два подхода к этому:
ActorFlow.actorRef play для возврата основного актера. Раньше был аналогичное обсуждение, вот суть. Если вы помещаете базовый субъект в карту (пользователь, веб-сокет), убедитесь, что используете поточно-ориентированную реализацию, такую как TrieMap.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 классический автобус событий, который работает локально. Для этого подхода к масштабированию вам потребуется реальная очередь сообщений за кулисами.
Удалось ли вам этого добиться? Я тоже столкнулся с той же проблемой.