Akka的基于actor的自定义事件总线实现导致瓶颈

时间:2016-01-16 15:33:41

标签: scala akka publish-subscribe mqtt akka-cluster

我正在尝试在Akka的actor模型之上实现Event Bus(Pub-Sub)模式。

“Native”EventBus实现不符合我的一些要求(例如,在主题中仅保留最后一条消息的可能性,它特定于MQTT协议,我正在为它实现消息代理{{3} })。

我的EventBus的当前界面如下:

object Bus {
  case class Subscribe(topic: String, actor: ActorRef)
  case class Unsubscribe(topic: String, actor: ActorRef)
  case class Publish(topic: String, payload: Any, retain: Boolean = false)
}

用法如下:

val system = ActorSystem("System")
val bus = system.actorOf(Props[MqttEventBus], name = "bus")
val device1 = system.actorOf(Props(new DeviceActor(bus)))
val device2 = system.actorOf(Props(new DeviceActor(bus)))

所有设备都引用了一个总线actor。总线参与者负责存储所有订阅状态和主题(例如保留消息)。

内部的

设备角色可以决定发布,订阅或取消订阅主题。

经过一些性能基准后,我意识到我当前的设计会影响发布和订阅之间的处理时间,原因如下:

  1. 我的EventBus实际上是一个单身人士
  2. 它造成了巨大的处理负载队列
  3. 如何为我的事件总线实施分发(并行化)工作负载? 目前的解决方案是否适合akka-cluster?

    目前,我正在考虑通过以下几种Bus实例https://github.com/butaji/JetMQ

    val paths = (1 to 5).map(x => {
      system.actorOf(Props[EventBusActor], name = "event-bus-" + x).path.toString
    })
    
    val bus_publisher = system.actorOf(RoundRobinGroup(paths).props())
    val bus_manager = system.actorOf(BroadcastGroup(paths).props())
    

    其中:

    • bus_publisher 将负责获取发布,
    • bus_manager 将负责获取订阅/取消订阅。

    如下所示,它将在所有总线上复制状态,并通过负载分配减少每个actor的队列。

1 个答案:

答案 0 :(得分:1)

您可以在单身公交车内部而不是在外面行驶。您的总线可能负责路由消息和建立主题,而子Actors可能负责分发消息。一个基本示例,演示了我所描述的内容,但没有取消订阅功能,重复订阅检查或监督:

import scala.collection.mutable
import akka.actor.{Actor, ActorRef}

class HashBus() extends Actor {
  val topicActors = mutable.Map.empty[String, ActorRef]

  def createDistributionActor = {
    context.actorOf(Props[DistributionActor])
  }

  override def receive = {
    case subscribe : Subscribe =>
      topicActors.getOrElseUpdate(subscribe.topic, createDistributionActor) ! subscribe

    case publish : Publish =>
      topicActors.get(topic).foreach(_ ! publish)
  }
}

class DistributionActor extends Actor {

  val recipients = mutable.List.empty[ActorRef]

  override def receive = {
    case Subscribe(topic: String, actorRef: ActorRef) =>
      recipients +: actorRef

    case publish : Publish =>
      recipients.map(_ ! publish)
  }
}

这将确保您的总线Actor邮箱不会饱和,因为总线的工作只是进行哈希查找。 DistributionActors将负责映射收件人并分发有效负载。同样,DistributionActor可以保留主题的任何状态。