Introduction to Akka Streams [Part-II]

23
By: Harmeet Singh (Taara) Sr. Software Consultant

Transcript of Introduction to Akka Streams [Part-II]

Page 1: Introduction to Akka Streams [Part-II]

By:Harmeet Singh (Taara)Sr. Software Consultant

Page 2: Introduction to Akka Streams [Part-II]

Agenda

1. Flows

2. Operator Fusion.

3. Graph DSL.

4. Leftover.

Page 3: Introduction to Akka Streams [Part-II]

Akka Stream: Flows

The way to perform transformations to the data as it flows between Source and Sink.

Page 4: Introduction to Akka Streams [Part-II]

Akka Stream: Flows

Page 5: Introduction to Akka Streams [Part-II]

Akka Stream: Example

object StreamingCopyApp extends App {

  val spath = Paths.get("/home/harmeet/akka­stream/knolx.log")  val source: Source[ByteString, Future[IOResult]] = FileIO.fromPath(spath)

  val dpath = Paths.get("/home/harmeet/akka­stream­copy")  val sink: Sink[ByteString, Future[IOResult]] = FileIO.toPath(dpath)

  val runnableGraph: RunnableGraph[Future[IOResult]] = source.to(sink)

  implicit val system = ActorSystem("akka­stream")  implicit val ec = system.dispatcher  implicit  val materializer = ActorMaterializer()

  runnableGraph.run().foreach { result =>    println(s"${result.status}, ${result.count} bytes read")    system.terminate()  }}

Page 6: Introduction to Akka Streams [Part-II]

Akka Stream: Flow Example

val frame: Flow[ByteString, String, NotUsed] = Framing.delimiter(ByteString("\n"), 10240).map(_.decodeString("UTF8"))

val parse: Flow[String, Event, NotUsed] = Flow[String].map(Event.parsing)

val filter: Flow[Event, Event, NotUsed] = Flow[Event].filter(_.log == "DEBUG")

val serialize: Flow[Event, ByteString, NotUsed] = Flow[Event].map{ event =>    ByteString(event.toJson.compactPrint)}

val composedFlow: Flow[ByteString, ByteString, NotUsed] = frame.via(parse)    .via(filter)    .via(serialize)

val runnableGraph: RunnableGraph[Future[IOResult]] = source.via(composedFlow).toMat(sink) (Keep.right)

Page 7: Introduction to Akka Streams [Part-II]

Akka Stream: Flow Example

val composedFlow: Flow[ByteString, ByteString, NotUsed] = Framing.delimiter(ByteString("\n"), 10240)

    .map(_.decodeString("UTF8"))    .map(Event.parsing)    .filter(_.log == "DEBUG")    .map { event =>      ByteString(event.toJson.compactPrint)    }

val runnableGraph: RunnableGraph[Future[IOResult]] = source.via(composedFlow)    .toMat(sink) (Keep.right)

Page 8: Introduction to Akka Streams [Part-II]

Akka Stream: Operator Fusion

Operator Fusion is used to performance optimization to mitigate the cost of passing elements between the

different stages.

Two main consequences are:

1. Passing elements fused stages faster.2. Fused stages no longer run in parallel.

Page 9: Introduction to Akka Streams [Part-II]

Akka Stream: Operator Fusion

Q. How can we execute fused stages in parallel???

Ans:1. By using async method2. Turn off auto-fusing (akka.stream.materializer.auto­

fusing=off)

Example:

Source(List(1, 2, 3)).map(_ + 1).async.map(_ * 2).to(Sink.ignore)

Page 10: Introduction to Akka Streams [Part-II]

Akka Stream: Operator Fusion

Page 11: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL

What if your data flow cannot be modeled as a simple linear process?

What if process is better modeled as computation graph instead?

Page 12: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL

The Graph DSL is kind of diagram ASCII art – in many cases you could translate a white board diagram of a graph into

the DSL.

A graph problem is one that typically involves the concept of fanning out and fanning in.

● Fanning out: Single input to multiple output.

● Fanning in: Multiple inputs single output.

Page 13: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

val in = Source(1 to 5)

  

      val f1 = Flow[Int].map(_ * 2)

   val f2 = Flow[Int].map(_ * 1)

Page 14: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

val f3 = Flow[Int].map(_ * 2)

  

     val f4 = Flow[Int].map(_ + 1)

val out = Sink.foreach[Int](println)

Page 15: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

val bcast = builder.add(Broadcast[Int](2))

val merge = builder.add(Merge[Int](2))

Page 16: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

 in ~> f1 ~> bcast ~> f2 ~> merge ~> f4 ~> out

Page 17: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

       bcast ~> f3 ~> merge

Page 18: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL Example

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

      val in = Source(1 to 5)      val out = Sink.foreach[Int](println)      val f1 = Flow[Int].map(_ * 2)      val f2 = Flow[Int].map(_ * 1)      val f3 = Flow[Int].map(_ * 2)      val f4 = Flow[Int].map(_ + 1)

      val bcast = builder.add(Broadcast[Int](2))      val merge = builder.add(Merge[Int](2))

      in ~> f1 ~> bcast ~> f2 ~> merge ~> f4 ~> out      bcast ~> f3 ~> merge      ClosedShape  })

Page 19: Introduction to Akka Streams [Part-II]

Akka Stream: Graph DSL ClosedShape

The graph itself was fully self-contained and complete.

OR

It does not have any open input port and output port like a closed circuit.

Page 20: Introduction to Akka Streams [Part-II]

Akka Stream: Partial Graphs

Whenever we need to create a reusable component for graph, which we can use according to requirements with

existing or new graph.

https://doc.akka.io/docs/akka/2.5/scala/stream/stream-graphs.html#constructing-and-combining-partial-graphs

Page 21: Introduction to Akka Streams [Part-II]

Leftovers

➔ API’s

➔ Error Handling.

➔ Streaming HTTP

➔ Handling IO Stream

➔ Unit Testing

Page 23: Introduction to Akka Streams [Part-II]