Akka Actor pubsub:在一个EventStream中使用多个参数进行订阅?

时间:2012-09-13 22:34:27

标签: scala akka

作为参考,这个问题源于Scala method performance (collect or foreach or other) looping through sockets?

我在actor中存储了对websocket的引用,然后将该actor订阅到Akka EventStream:

val socketActor = system.actorOf(Props(new Actor {
  val socket = WebSocketConnection

  def receive = {
    case d: AppMessage ⇒ socket.send(d)
  }
}))
system.eventStream.subscribe(socketActor, classOf[AppMessage])

我错误的是,我可以使用EventStream创建的唯一分类器是类类型。因此,如果您想将消息路由到不同的actor,比如基于userId,您是否需要创建多个EventStream并手动构建一个EventBus,或者这里有什么我缺少的东西?

如果我能做一些简单的事情会很好:

system.eventStream.subscribe(socketActor, Map("userId" -> userId, "teamId" -> teamId) )

这可能仅仅是一个概念性问题,因为我不太确定EventStream代表什么。

2 个答案:

答案 0 :(得分:3)

这是我的解决方案,基于ActorEventBus基于此要点:https://gist.github.com/3757237

我发现这比处理EventStreams更具可维护性。未来可能需要多个EventStream,但此时它很容易支持当前的基础架构。

MessageBus

首先,MessageBus根据PubSub频道处理包含在actor中的套接字的传出消息:

case class MessageEvent(val channel:String, val message:String)

/**
 * message bus to route messages to their appropriate contexts
 */
class MessageBus extends ActorEventBus with LookupClassification {

    type Event = MessageEvent
  type Classifier = String

  protected def mapSize(): Int = {
    10
  }

  protected def classify(event: Event): Classifier = {
    event.channel
  }

  protected def publish(event: Event, subscriber: Subscriber): Unit = {
    subscriber ! event
  }

}


object MessageBus {

  val actorSystem = ActorSystem("contexts")
  val Bus = new MessageBus

  /**
   * create an actor that stores a browser socket
   */
  def browserSocketContext(s: WebSocketConnection, userId: Long, teamId: Long) = {
    val subscriber = actorSystem.actorOf(Props(new BrowserSocket(s,userId,teamId)))

    Bus.subscribe( subscriber, "/app/socket/%s" format s.toString)
    Bus.subscribe( subscriber, "/app/browser/u/%s" format userId )
    Bus.subscribe( subscriber, "/app/browser/t/%s" format teamId )
    Bus.subscribe( subscriber, "/app/browser" )
  }
}

使用Actors进行套接字访问

这是实际包含套接字的actor:

/**
 * actor wrapping access for browser socket
 */
class BrowserSocket(
  val s: WebSocketConnection,
  val userId: Long,
  val teamId: Long

) extends Actor {

  def receive = {
    case payload:MessageEvent => 
      s.send(payload.message)

    case ping:MessagePing =>
      s.ping(ping.data)

  }

}

答案 1 :(得分:1)

据我所知,EventStreams和Event Bus用于记录和监控。您通常使用Actors构建所需的功能并在它们之间传递消息。

因此,您将AppMessage发送给自定义路由器角色,该角色将分拣要发送给哪个后备角色。也许路由器可以在它认为合适时产生后台演员,或者演员可以在路由器上订阅(通过传递适当的消息)。这主要取决于您需要实施的逻辑。