3

I'm having problems in getting Publishers and Subscribers out of my flows when using more complicated graphs. My goal is to provide an API of Publishers and Subscribers and run the Akka streaming internally. Here's my first try, which works just fine.

val subscriberSource = Source.subscriber[Boolean]
val someFunctionSink = Sink.foreach(Console.println)

val flow = subscriberSource.to(someFunctionSink)

//create Reactive Streams Subscriber
val subscriber: Subscriber[Boolean] = flow.run()

//prints true
Source.single(true).to(Sink(subscriber)).run()

But then with a more complicated broadcast graph, I'm unsure as how to get the Subscriber and Publisher objects out? Do I need a partial graph?

val subscriberSource = Source.subscriber[Boolean]
val someFunctionSink = Sink.foreach(Console.println)
val publisherSink = Sink.publisher[Boolean]

FlowGraph.closed() { implicit builder =>
  import FlowGraph.Implicits._

  val broadcast = builder.add(Broadcast[Boolean](2))

  subscriberSource ~> broadcast.in
  broadcast.out(0) ~> someFunctionSink
  broadcast.out(1) ~> publisherSink
}.run()

val subscriber: Subscriber[Boolean] = ???
val publisher: Publisher[Boolean] = ???
ripla
  • 422
  • 4
  • 12

1 Answers1

8

When you call RunnableGraph.run() the stream is run and the result is the "materialized value" for that run.

In your simple example the materialized value of Source.subscriber[Boolean] is Subscriber[Boolean]. In your complex example you want to combine materialized values of several components of your graph to a materialized value that is a tuple (Subscriber[Boolean], Publisher[Boolean]).

You can do that by passing the components for which you are interested in their materialized values to FlowGraph.closed() and then specify a function to combine the materialized values:

import akka.stream.scaladsl._
import org.reactivestreams._

val subscriberSource = Source.subscriber[Boolean]
val someFunctionSink = Sink.foreach(Console.println)
val publisherSink = Sink.publisher[Boolean]

val graph =
  FlowGraph.closed(subscriberSource, publisherSink)(Keep.both) { implicit builder ⇒
    (in, out) ⇒
      import FlowGraph.Implicits._

      val broadcast = builder.add(Broadcast[Boolean](2))

      in ~> broadcast.in
      broadcast.out(0) ~> someFunctionSink
      broadcast.out(1) ~> out
  }
val (subscriber: Subscriber[Boolean], publisher: Publisher[Boolean]) = graph.run()

See the Scaladocs for more information about the overloads of FlowGraph.closed.

(Keep.both is short for a function (a, b) => (a, b))

jrudolph
  • 8,307
  • 4
  • 32
  • 50
  • Thank you for the informative answer. What threw me off was the size of GraphApply, it's a bit of a monster. Also didn't realize that with the signature `def closed[Mat, M1, M2](g1: Graph[Shape, M1], g2: Graph[Shape, M2])(combineMat: (M1, M2) ⇒ Mat)(buildBlock: FlowGraph.Builder[Mat] ⇒ (g1.Shape, g2.Shape) ⇒ Unit): RunnableGraph[Mat]` the materialized value could be the tuple I need. – ripla Jul 03 '15 at 19:31
  • Just as an additional note if someone tries to copy the second example code: you need to use fanoutPublisher instead of publisher to make it work. My guess is that building the graph somehow uses the one subscriber slot that's allocated. – ripla Jul 03 '15 at 21:59
  • @ripla Do you've a working example of using a `fanoutPublisher`? Yours is one of the 2 references that I found on the internet about a `fanoutPublisher`, that I'm trying to implement, but none with actual code sample. I posted a [question](https://groups.google.com/forum/#!topic/akka-user/UznPhKEjLWA) in the Akka user group too but so far no luck yet. – Abhijit Sarkar Oct 05 '15 at 04:29