Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to create multiple chatrooms using websockets in scala?

I'm trying to learn how to use WebSockets and Akka using the Chat example in the Play for Scala book.

In the book, there is one "ChatRoom" being created, and that's instantiated in the Chat controller with something as simple as this:

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

I want to expand this example and have multiple chat rooms available instead of just one. A user can provide a string, which can be a chatroom "name", and that would create a new chatroom. Anyone that tries to join this chatroom would share a broadcast with each other, but not with people in another chatroom. Very similar to IRC.

My questions are the following:

1: How do I create a ChatRoom with a unique name if one does not already exist? 2: How can I check if the existing ChatRoom exists and get a reference to it?

The chatroom name will come via either the URL or a query parameter, that part will be trivial. I'm just not entirely sure how to uniquely identify the Akka ChatRoom and later retrieve that Actor by name.

like image 671
harish Avatar asked Apr 03 '14 10:04

harish


2 Answers

You can name actors in Akka, so instead of having:

Akka.system.actorOf(Props[ChatRoom])

You would have:

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

Then, depending on the Akka version you're using, use either Akka.system.actorFor("room1") or Akka.system.actorSelection("room1") to get a reference to the wanted chat room.

like image 53
Peter Avatar answered Nov 15 '22 01:11

Peter


Use the Akka EventBus trait. You can use eventBus and LookupClassification to implement topic based publish-subscribe where the "topic" is the roomID and the subscribers are either the actors for each room or the web-socket actors for each client.

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

}

Then register your actors (in reality you would keep a count of how many users are in each room and subscribe when users enter the room and unsubscribe and kill the actor when no-one is in the room)

val lookupBus = new LookupBusImpl
lookupBus.subscribe(room1Actor, "room1")
lookupBus.subscribe(room2Actor, "room2")

The message will be switched based on the roomID

lookupBus.publish(MsgEnvelope("room1", "hello room1"))
lookupBus.publish(MsgEnvelope("room2", "hello room2"))
lookupBus.publish(MsgEnvelope("room3", "hello dead letter"))
like image 38
Jake Avatar answered Nov 15 '22 00:11

Jake