Akka Stream - 将流分成多个来源

时间:2017-05-14 10:21:40

标签: scala akka akka-stream

我在Akka Stream中有一个以Sink结尾的TCP连接。现在所有消息都进入一个接收器。我希望在给定功能的情况下将流分割为未知数量的接收器。

用例如下,从TCP连接我得到List[DeltaValue]之类的连续流,现在我想为每个DeltaValue.id创建一个actorSink,这样我就可以不断累积和实现每个DeltaValue.id的行为。我发现这是流处理中的标准用例,但我无法找到Akka Stream的一个好例子。

这就是我现在所拥有的:

def connect(): ActorRef = tcpConnection
    .//SOMEHOW SPLIT HERE and create a ReceiverActor for each message
    .to(Sink.actorRef(system.actorOf(ReceiverActor.props(), ReceiverActor.name), akka.Done))
    .run()

更新: 我现在有了这个,不知道该怎么说,它感觉不太稳定,但应该有效:

  private def spawnActorOrSendMessage(m: ResponseMessage): Unit = {
    implicit val timeout = Timeout(FiniteDuration(1, TimeUnit.SECONDS))
    system.actorSelection("user/" + m.id.toString).resolveOne().onComplete {
      case Success(actorRef) => actorRef ! m
      case Failure(ex) => (system.actorOf(ReceiverActor.props(), m.id.toString)) ! m
    }
  }

  def connect(): ActorRef = tcpConnection
    .to(Sink.foreachParallel(10)(spawnActorOrSendMessage))
    .run()

2 个答案:

答案 0 :(得分:1)

<强> EventStream

您可以在流水槽中向ActorSystem的EventStream发送消息,并分别让Actors订阅该流。

在流级别拆分

您可以使用Broadcast在流级别拆分流。 documentation就是一个很好的例子。

在演员层面拆分

您还可以将Sink.actorRefBroadcastPool结合使用,将消息广播给多个演员。

答案 1 :(得分:1)

以下内容应该是问题中更新内容的稍微改进版本。主要的改进是您的actor保持在数据结构中,以避免每个传入消息的actorSelection解析。

  case class DeltaValue(id: String, value: Double)

  val src: Source[DeltaValue, NotUsed] = ???

  src.runFold(Map[String, ActorRef]()){
    case (actors, elem) if actors.contains(elem.id) ⇒
      actors(elem.id) ! elem.value
      actors
    case (actors, elem) ⇒
      val newActor = system.actorOf(ReceiverActor.props(), ReceiverActor.name)
      newActor ! elem.value
      actors.updated(elem.id, newActor)
  }

请记住,当您将Akka Streams与裸演员集成时,您会失去背压支持。这就是为什么你应该尽可能尝试在Akka Streams的边界内实现逻辑的原因之一。这并不总是可行的 - 例如何时需要远程处理等。

在您的情况下,您可以考虑利用groupBysubstream的概念。下面的例子是通过对它们求和来折叠每个子流的元素,只是为了给出一个想法:

  src.groupBy(maxSubstreams = Int.MaxValue, f = _.id)
    .fold("" → 0d) {
      case ((id, acc), delta) ⇒ id → delta.value + acc
    }
    .mergeSubstreams
    .runForeach(println)