Scala 在Play Framework WebSocket中广播消息
我正在Play Framework WebSockets中使用Scala 在Play Framework WebSocket中广播消息,scala,websocket,playframework-2.0,playframework-2.2,iterate,Scala,Websocket,Playframework 2.0,Playframework 2.2,Iterate,我正在Play Framework WebSockets中使用Concurrent.unicast[JsValue]推送消息,我希望优化向多个用户发送相同消息的过程。是否可以以某种方式使用多个并发频道?简短回答 长答案 package controllers import akka.actor.Actor import play.api.libs.iteratee.Enumerator import play.api.libs.iteratee.Concurrent.Channel impo
Concurrent.unicast[JsValue]
推送消息,我希望优化向多个用户发送相同消息的过程。是否可以以某种方式使用多个并发频道
?简短回答
长答案
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)
}
}
}
}
该过滤器必须应用于:
def chat(group_id: String) = WebSocket.using[JsValue] { request =>
val in = Iteratee.foreach[JsValue]{ msg=>
public_channel.push(msg)
}
(in, public_enumerator &> filter(group_id))
}
简短回答
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)
}
}
}
}
为每个用户维护单独的频道,并使组与用户关联
长答案
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)
}
}
}
}
根据我的实验,
Concurrent.broadcast
并没有发送给每个人(可能是一些不幸的命名?)
以下是我所使用的与预期一样有效的方法
package controllers
import play.api._
import play.api.mvc._
import play.api.libs.iteratee.Concurrent
import play.api.libs.iteratee.Iteratee
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.collection.mutable.{Set => MS}
import scala.concurrent._
object Application extends Controller {
val c:MS[(Int, Concurrent.Channel[String])] = MS() // (channelID, Channel))
def pushHello = c.foreach(_._2.push("hello")) // push to ALL channels
def index = WebSocket.async[String] { _ => future{
val (out,channel) = Concurrent.broadcast[String]
val channelID = scala.util.Random.nextInt
c.add((channelID, channel))
val in = Iteratee.foreach[String] {
_ match {
case any => channel.push("received:"+any) // push to current channel
}
}.map { _ => c.retain(x => x._1 != channelID) }
(in, out)
}
}
}
Concurrent.broadcast
做你想做的事吗?broadcast会将此消息发送给所有人,如果我错了,请纠正我。例如,我想将数据发送给25人中的5人。您必须对枚举数应用筛选器。如果邮件包含收件人的任何信息,这将起作用。这将向所有用户发送邮件,只向部分用户发送数据,如果我有一个10.000,我只想向100发送,单独向100发送相同的邮件,这不是最有效的方式,这就是为什么我在寻找一些广播解决方案。但是如果我有10000个用户,我向其中20个用户发送消息,然后我必须检查所有这10000条消息,这对我来说不是很优化,也许有某种方式可以组合频道或类似的东西
package controllers
import play.api._
import play.api.mvc._
import play.api.libs.iteratee.Concurrent
import play.api.libs.iteratee.Iteratee
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.collection.mutable.{Set => MS}
import scala.concurrent._
object Application extends Controller {
val c:MS[(Int, Concurrent.Channel[String])] = MS() // (channelID, Channel))
def pushHello = c.foreach(_._2.push("hello")) // push to ALL channels
def index = WebSocket.async[String] { _ => future{
val (out,channel) = Concurrent.broadcast[String]
val channelID = scala.util.Random.nextInt
c.add((channelID, channel))
val in = Iteratee.foreach[String] {
_ match {
case any => channel.push("received:"+any) // push to current channel
}
}.map { _ => c.retain(x => x._1 != channelID) }
(in, out)
}
}
}