Как осуществляется управление состоянием в Scala Play! 2.0 Веб-сокеты?

Я смотрю на пример на https://github.com/playframework/Play20/tree/master/samples/scala/websocket-chat

Чтобы создать контроллер веб-сокета, вы пишете что-то вроде:

def chat(username: String) = WebSocket.async[JsValue] { request  =>
    ChatRoom.join(username)
}  

Chatroom.join возвращает scala.concurrent.Future [(Iteratee [JsValue, _], Enumerator [JsValue])]. Но где же итератор и перечислитель, используемые в Play! рамки? Класс WebSocket (WebSocket.scala), похоже, игнорирует входные данные:

case class WebSocket[A](f: RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit)        (implicit val frameFormatter: WebSocket.FrameFormatter[A]) extends Handler {

  type FRAMES_TYPE = A

  /**
   * Returns itself, for better support in the routes file.
   *
   * @return itself
   */
   def apply() = this
}

Как играть! управлять изменяющимся состоянием итератора, когда он потребляет ввод?


person Mark    schedule 24.12.2012    source источник


Ответы (1)


Стоит отметить, что сам WebSocket - всего лишь тупой контейнер. Магия происходит в разных классах play.core.server.netty.

Чтобы понять, что это за магия, поучительно взглянуть на сигнатуру f (функция, содержащаяся в WebSocket:

RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit

Это функция, которая принимает RequestHeader, Enumerator и Iteratee и что-то с ними делает.

Итак, в какой-то момент в будущем фреймворк предоставит нашему WebSocket RequestHeader (который должен быть очевидным), Enumerator[A] (перечислители являются источниками, в данном случае это сообщения, получаемые от клиента) и Iteratee[A, Unit] (Итераторы - это приемники, в этом случае мы отправляем сообщения, чтобы вернуться к клиенту).

В случае WebSocket.adapter WebSocket подключит Enumerator к Iteratee через Enumeratee. В случае WebSocket.using WebSocket подключит удаленный Enumerator к локальному Iteratee, а удаленный Iteratee к локальному Enumerator.

Вместо прямого определения WebSocket, вероятно, будет проще использовать один из удобных методов в объекте WebSocket. Следующий код будет повторять предыдущее полученное сообщение:

  def mySocket = WebSocket.adapter {implicit req =>
    var lastMessage = "No previous message"
    Enumeratee.map[String] {msg =>
      val response = lastMessage
      lastMessage = msg
      response
    }
  }

Обратите внимание, что этот код почти наверняка имеет проблемы с безопасностью потоков - в Scala вы должны стараться избегать изменяемого состояния, когда это возможно, или использовать акторов или аналогичные, если нет.

Или попробуйте WebSocket.using и посмотрите на pushee Enumerator в сочетании с foreach Iteratee, хотя это немного скрипач. Возможно, понятно, что перечислитель pushee устарел в Play 2.1, поскольку он заменен новой системой каналов.

person James_pic    schedule 24.12.2012