Scala Akka流连接到多个接收器

Scala Akka流连接到多个接收器,scala,akka,akka-stream,reactive-streams,Scala,Akka,Akka Stream,Reactive Streams,我在akka stream中实现了一个自定义组件,它将元素作为输入,根据一个键对它们进行分组和合并,并通过十几个出口中的一个发送出去。您可以将此组件视为一种GroupBy组件,它不会将流划分为子流,而是实际流。除了对传入元素进行分区外,它还将它们合并到一个元素中,即组件内部存在一些缓冲,因此1个元素输入并不一定意味着1个元素通过出口输出 下面是所述组件的简化实现 class CustomGroupBy[A,B](k: Int, f: A => Int) extends GraphStage

我在akka stream中实现了一个自定义组件,它将元素作为输入,根据一个键对它们进行分组和合并,并通过十几个出口中的一个发送出去。您可以将此组件视为一种GroupBy组件,它不会将流划分为子流,而是实际流。除了对传入元素进行分区外,它还将它们合并到一个元素中,即组件内部存在一些缓冲,因此1个元素输入并不一定意味着1个元素通过出口输出

下面是所述组件的简化实现

class CustomGroupBy[A,B](k: Int, f: A => Int) extends GraphStage[FlowShape[B, B]] {

  val in = Inlet[A]("CustomGroupBy.in")
  val outs = (0 until k).map(i => Outlet[B](s"CustomGroupBy.$i.out"))

  override val shape = new AmorphousShape(scala.collection.immutable.Seq(in), outs)

  /* ... */
}
我现在想知道如何将该组件的每个插座连接到不同的接收器,并将所有这些接收器的物化值结合起来

我用graph DSL做了一些尝试,但还没有完全成功。有没有人能为我提供一个这样做的片段,或者为我指出正确的方向


提前谢谢

您很可能想要内置舞台。示例用法如下所示:


您可能需要
akka.stream.scaladsl.Partition[T](输出端口:Int,分区器:T⇒ Int)

编辑:

要连接所有端口并保留具体化的值,必须将stage作为参数提供给
GraphDSL.create
方法

这允许您为具体化的值定义一个组合器,并将阶段作为参数添加到最后一个参数中。请注意,这个重载的
create
方法不接受varargs参数,因此可能无法以这种方式处理14个不同的阶段

假设您的阶段有一些名称,在3个输出的情况下,我将如何实现它:

val runnable = RunnableGraph.fromGraph(
  GraphDSL.create(
    source, customGroupBy, sink1, sink2, sink3)(combiner) {  //the combiner is the function to combine the materialized values
      implicit b => //this is the builder, needed as implicit to make the connections 
      (src, cgb, s1, s2, s3) => //here are the stages added to the builder
      import GraphDSL.Implicits._

      src.out ~> cgb.in
      List(s1, s2, s3).map(_.in).zip(cgb.outlets).foreach{
        case (in, out) => in ~> out
      }

      ClosedShape
    }
  )
)

请记住,如果您不需要其中一个阶段的物化值,您可以通过执行
val cgb=b.add(customGroupBy)

将其添加到DSL中,谢谢您的回答!不幸的是,我不希望广播,因为我计划可能有许多传出流,而且广播和过滤每个流似乎很昂贵。根据你的经验,它不贵吗?你能推荐点别的吗?不客气。不幸的是,我唯一的经历就是
广播
。复制您的
源代码可能更容易,然后您可以具体化N个流……您能给出一个简单的代码示例,说明为什么图形DSL不适合您吗?我想说的是,如果你在图表中连接了所有端口,它应该可以工作。是的,这似乎是我想要的。不过,由于正在进行的合并,我必须编写自己的组件,但是作为一个黑匣子,该组件看起来就像一个分区阶段。我的问题确实是将输出连接到接收器,然后组合来自每个接收器的物化值。很好!非常感谢。是否有一种方法可以在传递汇之前以某种方式组合汇,以创建汇或在汇内创建汇,从而摆脱22个最大参数的限制?让我澄清我之前的问题。我的每个接收器都具体化了一个ActorRef(对于ActorSubscriber)。我不需要实现ActorRefs,只需要等待所有ActorRefs被终止,这样我就知道工作已经完成。换句话说,如果我知道处理是通过其他方式完成的,我可能不需要汇的物化值。如果物化值是一个
ActorRef
,则它是同步创建的,因此流的物化确保了参与者的创建。如果它是一个
未来的[ActorRef]
,它会变得更复杂。一个丑陋的方法是将你的接收器列表分成更少的
AmorphousShape
s(使用
GraphDSL.create
),每个都有几个输入,但只有一个具体化的值,当它里面的所有接收器都完成时,它就完成了,然后把那些(更少的)形状放入你的主
GraphDSL.create
。它真的很难看,但它确实起作用了。是否没有其他方法来组合汇的物化值?也许解决方案是在GraphDSL.create之外进行,例如,创建一个参与者,在所有接收器的物化参与者上执行终止监视,然后使用该参与者形成一个“主”接收器,该接收器在其监视的所有参与者终止时完成?
val runnable = RunnableGraph.fromGraph(
  GraphDSL.create(
    source, customGroupBy, sink1, sink2, sink3)(combiner) {  //the combiner is the function to combine the materialized values
      implicit b => //this is the builder, needed as implicit to make the connections 
      (src, cgb, s1, s2, s3) => //here are the stages added to the builder
      import GraphDSL.Implicits._

      src.out ~> cgb.in
      List(s1, s2, s3).map(_.in).zip(cgb.outlets).foreach{
        case (in, out) => in ~> out
      }

      ClosedShape
    }
  )
)