Broadcasting messages in Play Framework WebSockets

后端 未结 4 1332
闹比i
闹比i 2021-01-07 05:53

I\'m pushing messages in Play Framework WebSockets using Concurrent.unicast[JsValue], and I want to optimize sending the same message to multiple users. Is it p

4条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-01-07 06:13

    Short Answer


    val (enumerator, channel) = Concurrent.broadcast[String]
    
    use above thing globally
    

    Long answer


    package controllers
    
    import play.api._
    import play.api.mvc._
    import play.libs.Akka
    import play.api.libs.concurrent.Execution.Implicits.defaultContext
    import play.api.libs.iteratee.Iteratee
    import play.api.libs.iteratee.Enumerator
    import akka.actor.Props
    import akka.pattern.ask
    import akka.util.Timeout
    
    import Room._
    
    import scala.concurrent.duration._
    
    object Application extends Controller {
    
      def index = Action {
        Ok(views.html.index("Your new application is ready."))
      }
    
      /**
       * get actor ref
       */
      val room = Akka.system.actorOf(Props[Room])
    
      /**
       * websocket action
       */
      def chat(name: String) = WebSocket.async[String](implicit request => {
        implicit val timeout = Timeout(1 seconds)
    
        (room ? Join(name)).mapTo[(Iteratee[String, _], Enumerator[String])]
      })
    }
    
    
    
    //Here is the actor
    package controllers
    
    import akka.actor.Actor
    import play.api.libs.iteratee.Concurrent
    import play.api.libs.iteratee.Iteratee
    import play.api.libs.concurrent.Execution.Implicits.defaultContext
    
    object Room {
      case class Join(name: String)
      case class Broadcast(msg: String)
      case object Quit
    }
    
    class Room extends Actor {
    
      /**
       * here is the meat 
       * Creating channel globally is important here
       * This can be accessed across all cases in receive method
       * pushing the message into this channel and returning this enumerator to all ,
       * broadcasts the message 
       */
      val (enumerator, channel) = Concurrent.broadcast[String]
    
      /**
       * keep track of users
       */
      val users = scala.collection.mutable.Set[String]()
    
      import Room._
    
      def receive = {
        case Join(name) => {
          /**
           * add new users
           */
          if(!users.contains(name)) {
            users += name
    
            val iteratee = Iteratee.foreach[String]{
              msg => {
                /**
                 * process messages from users
                 * here we are broadcast it to all other users
                 */
                self ! Broadcast(msg)
              }
            }.map( _ => {
              /**
               * user closed his websocket. 
               * remove him from users
               */
              users -= name
            })
    
            /**
             * send iteratee, enumerator pair to the sender of join message
             */
            sender ! (iteratee, enumerator)
          } else {
    
            /**
             * already added users
             */
            val iteratee = Iteratee.ignore[String]
    
            /**
             * send iteratee and enumerator pair
             */
            sender ! (iteratee, enumerator)
          }
        }
    
        case Broadcast(msg) => channel push(msg)
    
        /**
         * close the common channel only when actor is stopped
         */
        case Quit => channel eofAndEnd(); context.stop(self)
      }
    }
    

提交回复
热议问题