Broadcasting messages in Play Framework WebSockets

后端 未结 4 1397
闹比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:25

    Short answer

    Maintain separate channel for each user and have groups associated with users

    Long answer

    package controllers
    
    import akka.actor.Actor
    import play.api.libs.iteratee.Enumerator
    import play.api.libs.iteratee.Concurrent.Channel
    import play.api.libs.iteratee.Concurrent
    import play.api.Logger
    import play.api.libs.iteratee.Iteratee
    import play.api.libs.concurrent.Execution.Implicits.defaultContext
    
    object AdvancedRoomMessages {
      case class Join(name: String)
      case class BroadcastGroup(msg: String, gName: String)
      case class BroadcastAll(msg: String)
      case class AddGroup(gName: String)
      case class RemoveGroup(gName: String)
      case class AddUserToGroup(userName: String, gName: String)
      case class removeUserFromGroup(userName: String, gName: String)
    }
    
    
    class AdvancedRoom extends Actor {
      import scala.collection.mutable._
    
    
      /**
       * common channel for communication
       */
    
    
      val (enumerator, channel) = Concurrent.broadcast[String]
    
    
    
      /**
       * every user has his own channel
       */
      val users = Map[String, (Enumerator[String],Channel[String])]()
    
      /**
       * users can be grouped
       */
      val groups = Map[String, Option[Set[String]]]()
    
      import AdvancedRoomMessages._
    
      def receive = {
        case Join(name) => {
          /**
           * join request from the user
           */
          if(users contains name) {
            /**
             * existing user
             */
            val iteratee = Iteratee.ignore[String]
            sender ! ((iteratee, users(name)._1))
          }else {
            /**
             * join request from a new user
             */
    
            /**
             * create new broadcast channel
             */
            val (enumerator, channel) = Concurrent.broadcast[String]
            users += ((name, (enumerator, channel)))
            val iteratee = Iteratee.foreach[String](msg => {
              //do something with the message
            }).map{ _ => {
              /**
               * user closed his websocket client, so remove the user
               * warning ... also remove the corresponding user name in groups
               */
              users(name)._2.eofAndEnd()
              users -= name
            }}
            sender ! (iteratee, enumerator)
          }
        }
        case BroadcastGroup(msg, gName) => {
          groups(gName) match {
            case Some(gMates) => {
              gMates.foreach { person => users(person)._2.push(msg)}
            }
            case None => Logger.info("empty group") //ignore sending message
          }
        }
        case BroadcastAll(msg) => {
          channel push msg
        }
        case AddGroup(gName: String) => {
          groups += ((gName, None))
        }
        case RemoveGroup(gName: String) => {
          groups -= gName
        }
        case AddUserToGroup(userName, gName) => {
          groups(gName) match {
            case Some(gMates) => gMates += userName
            case None => Set(userName)
          }
        }
      }
    }
    

提交回复
热议问题