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
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)
}
}
}
}