如何在Scala中使用Websockets创建多个聊天室?

6
我正在学习如何使用WebSockets和Akka,在Play for Scala一书中使用Chat示例。在该书中,创建了一个“ChatRoom”,并且在Chat控制器中实例化,如下所示:

val room = Akka.system.actorOf(Props[ChatRoom])

我想扩展这个示例,并提供多个可用的聊天室而不仅仅是一个。用户可以提供一个字符串,即聊天室“名称”,然后会创建一个新的聊天室。尝试加入此聊天室的任何人都将共享广播,但不与其他聊天室中的人共享。与IRC非常相似。
我的问题如下: 1:如果不存在,则如何创建具有唯一名称的ChatRoom? 2:如何检查现有的ChatRoom是否存在,并获取对它的引用?
聊天室名称将通过URL或查询参数传递,这部分很简单。但我不确定如何唯一标识Akka ChatRoom,并稍后按名称检索该Actor。
2个回答

1
您可以在Akka中为演员命名,因此不必使用以下方式:
Akka.system.actorOf(Props[ChatRoom])

你会有:

你会有:

Akka.system.actorOf(Props[ChatRoom],"room1")

然后,根据您使用的Akka版本,使用Akka.system.actorFor("room1")Akka.system.actorSelection("room1")来获取所需聊天室的引用。


如果有两个房间,如何获取房间引用。 - harish
在将它们命名不同之后,您可以使用actorFor或actorSelection来获取每个房间的引用,然后向其发送消息。 - Peter
我正在尝试使用以下代码:Akka.system.actorOf(Props[ChatRoom],"room1"),但是当我使用Akka.system.actorSelection("room1")时,新成员无法加入房间。 - harish
actorSelection 只会返回表示给定房间的 ActorRef,您需要向其发送加入消息。 - Peter
val first = Akka.system.actorSelection("room1") (first ? Join(username)).map { case Connected(enumerator) => // 创建一个迭代器来消费数据 val iteratee = Iteratee.foreach[JsValue] { event => first ! Talk(username, (event \ "text").as[String]) }.map { _ => first ! Quit(username) }(iteratee, enumerator)} 我使用了这段代码但是它没有起作用。 - harish
什么出了问题?你看到哪里有错误吗?你能编辑你的问题并将代码发布到聊天室吗? - Peter

1
使用Akka EventBus特征。您可以使用eventBus和LookupClassification来实现基于主题的发布-订阅,其中“主题”是roomID,订阅者可以是每个房间的演员或每个客户端的Web套接字演员。
import akka.event.EventBus
import akka.event.LookupClassification

final case class MsgEnvelope(topic: String, payload: Any)

/**
 * Publishes the payload of the MsgEnvelope when the topic of the
 * MsgEnvelope equals the String specified when subscribing.
 */
class LookupBusImpl extends EventBus with LookupClassification {
  type Event = MsgEnvelope
  type Classifier = String
  type Subscriber = ActorRef

  // is used for extracting the classifier from the incoming events  
  override protected def classify(event: Event): Classifier = event.topic

  // will be invoked for each event for all subscribers which registered themselves
  // for the event’s classifier
  override protected def publish(event: Event, subscriber: Subscriber): Unit = {
    subscriber ! event.payload
  }

  // must define a full order over the subscribers, expressed as expected from
  // `java.lang.Comparable.compare`
  override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
    a.compareTo(b)

  // determines the initial size of the index data structure
  // used internally (i.e. the expected number of different classifiers)
  override protected def mapSize: Int = 128

}

然后注册您的演员(实际上,您将计算每个房间中有多少用户,并在用户进入房间时订阅,当没有人在房间时取消订阅并终止演员)。
val lookupBus = new LookupBusImpl
lookupBus.subscribe(room1Actor, "room1")
lookupBus.subscribe(room2Actor, "room2")

根据房间ID,消息将会被切换

lookupBus.publish(MsgEnvelope("room1", "hello room1"))
lookupBus.publish(MsgEnvelope("room2", "hello room2"))
lookupBus.publish(MsgEnvelope("room3", "hello dead letter"))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接