我在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()
答案 0 :(得分:1)
<强> EventStream 强>
您可以在流水槽中向ActorSystem的EventStream
发送消息,并分别让Actors订阅该流。
在流级别拆分
您可以使用Broadcast在流级别拆分流。 documentation就是一个很好的例子。
在演员层面拆分
您还可以将Sink.actorRef
与BroadcastPool
结合使用,将消息广播给多个演员。
答案 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的边界内实现逻辑的原因之一。这并不总是可行的 - 例如何时需要远程处理等。
在您的情况下,您可以考虑利用groupBy
和substream的概念。下面的例子是通过对它们求和来折叠每个子流的元素,只是为了给出一个想法:
src.groupBy(maxSubstreams = Int.MaxValue, f = _.id)
.fold("" → 0d) {
case ((id, acc), delta) ⇒ id → delta.value + acc
}
.mergeSubstreams
.runForeach(println)