Scala 合并和广播,构建(简单的)Akka图

Scala 合并和广播,构建(简单的)Akka图,scala,akka,Scala,Akka,Akka文档非常丰富,有很多教程。但要么它们过时了,要么它们只涵盖了基本内容(或者,也许我就是找不到合适的) 我想创建一个websocket应用程序,在服务器端有多个客户端和多个源。因为我不想从一开始就不知所措,所以我想迈出一小步,逐步增加我正在构建的软件的复杂性 在玩弄了一些简单的流之后,我现在想从一个更复杂的图开始 我想要的是: 两个源,一个用于将“keepAlive”消息从服务器推送到客户端(目前只有一个),另一个用于实际推送有用数据 现在我有了第一个: val tickingSource

Akka文档非常丰富,有很多教程。但要么它们过时了,要么它们只涵盖了基本内容(或者,也许我就是找不到合适的)

我想创建一个websocket应用程序,在服务器端有多个客户端和多个源。因为我不想从一开始就不知所措,所以我想迈出一小步,逐步增加我正在构建的软件的复杂性

在玩弄了一些简单的流之后,我现在想从一个更复杂的图开始

我想要的是:

两个源,一个用于将“keepAlive”消息从服务器推送到客户端(目前只有一个),另一个用于实际推送有用数据

现在我有了第一个:

val tickingSource: Source[Array[Byte], Cancellable] =
  Source.tick(initialDelay = 1 second, interval = 10 seconds, tick = NotUsed)
        .zipWithIndex
        .map{ case (_, counter) => SomeMessage().toByteArray}
其中,
SomeMessage
是protobuf类型

因为我找不到添加演员作为源的最新方法,所以我在第二个源中尝试了以下方法:

val secondSource = Source(1 to 1000)
val secondSourceConverter = Flow[Int].map(x => BigInteger.valueOf(x).toByteArray)
我对图表的尝试:

val g: RunnableGraph[NotUsed] = RunnableGraph.fromGraph(GraphDSL.create()
{
  implicit builder =>
  import GraphDSL.Implicits._

  val sourceMerge = builder.add(Merge[Array[Byte]](2).named("sourceMerge"))

  val x = Source(1 to 1000)

  val y = Flow[Int].map(x => BigInteger.valueOf(x).toByteArray)

  val out = Sink.ignore
  tickingSource ~> sourceMerge ~> out
  x ~> y ~> sourceMerge

  ClosedShape
})

现在
g
属于
RunnableGraph[NotUsed]
类型,而对于我的websocket,它应该是
RunnableGraph[Array[Byte]]
。我想知道:我是否已经做了一些完全错误的事情?

我不确定您希望如何处理传入的消息,但这里有一个简单的示例。希望它能帮助你

path("ws") {
   extractUpgradeToWebSocket { upgrade =>
    complete {
      import scala.concurrent.duration._
      val tickSource = Source.tick(1.second, 1.second, TextMessage("ping"))
      val messagesSource = Source.queue(10, OverflowStrategy.backpressure)
      messagesSource.mapMaterializedValue { queue =>
        //do something with out queue
        //like myHandler ! RegisterOutQueue(queue)
      }
      val sink = Sink.ignore
      val source = tickSource.merge(messagesSource)
      upgrade.handleMessagesWithSinkSource(
        inSink = sink,
        outSource = source
      )
   }
}

您需要将
secondSourceConverter
传递到
GraphDSL.create
中,如下面的示例所示。在这里,他们导入了2个接收器,但这是相同的技术

RunnableGraph.fromGraph(GraphDSL.create(topHeadSink, bottomHeadSink)((_, _)) { implicit builder =>
  (topHS, bottomHS) =>
  import GraphDSL.Implicits._
  val broadcast = builder.add(Broadcast[Int](2))
  Source.single(1) ~> broadcast.in

  broadcast.out(0) ~> sharedDoubler ~> topHS.in
  broadcast.out(1) ~> sharedDoubler ~> bottomHS.in
  ClosedShape
})

您的图形属于
RunnableGraph[NotUsed]
类型,因为您正在使用
Sink.ignore
。您可能需要一个
RunnableGraph[Future[Array[Byte]]]
而不是
RunnableGraph[Array[Byte]]

val byteSink = Sink.fold[Array[Byte], Array[Byte]](Array[Byte]())(_ ++ _)

val g = RunnableGraph.fromGraph(GraphDSL.create(byteSink) { implicit builder => bSink =>
  import GraphDSL.Implicits._

  val sourceMerge = builder.add(Merge[Array[Byte]](2))
  tickingSource ~> sourceMerge ~> bSink.in
  secondSource ~> secondSourceConverter ~> sourceMerge

  ClosedShape
})
// RunnableGraph[Future[Array[Byte]]]

Source.queue帮助我替换了我的参与者源。@kardapoltsev如何将对源的引用传递给参与者?你的答案与此无关。他在询问如何构建一个图表,你在展示如何使用Akka HTTP。实际上,
mapMaterializedValue
返回一个新的源代码。但是非常感谢:)仍然可以得到一个
RunnableGraph[(可取消,不使用)]
:(请发布一个示例。