Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Monitoring (Akka) Streams

Monitoring (Akka) Streams

Ok, you got your Akka Streams pipeline finally deployed to production. But how are you monitoring it? When productionising Reactive Streams, the same backpressure that preserves the safety of your pipeline can get in the way of effectively monitoring its status. In this talk we’ll present a line of action to get a pulse on your Akka Streams app, to better understand its throughput and bottlenecks.

Stefano Bonetti

July 26, 2018
Tweet

More Decks by Stefano Bonetti

Other Decks in Programming

Transcript

  1. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. TRANSPARENT PROCESSORS 14 Processor[T,

    S] Processor[T, S] Publisher[T] Subscriber[S] c1 c2 c3 c4 Processor[T, T] Processor[S, S]
  2. val pangram = Source(List("The", "quick", "brown", "fox", "jumps", "over", "the",

    "lazy", "dog")) val toChars = Flow[String].mapConcat(_.toList) val print = Sink.foreach(println) pangram .via(toChars) .runWith(print) AKKA STREAMS - THROUGHPUT
  3. val pangram = Source(List("The", "quick", "brown", "fox", "jumps", "over", "the",

    "lazy", "dog")) val toChars = Flow[String].mapConcat(_.toList) val print = Sink.foreach(println) pangram .via(toChars) .runWith(print) AKKA STREAMS - THROUGHPUT
  4. val pangram = Source(List("The", "quick", "brown", "fox", "jumps", "over", "the",

    "lazy", "dog")) val toChars = Flow[String].mapConcat(_.toList) val print = Sink.foreach(println) def meter[T](name: String): Flow[T, T, NotUsed] = { val msgCounter = Kamon.metrics.counter(name) Flow[T].map { x ⇒ msgCounter.increment(); x } } pangram .via(meter("produced")) .via(toChars) .via(meter("processed")) .runWith(print) AKKA STREAMS - THROUGHPUT
  5. val pangram = Source(List("The", "quick", "brown", "fox", "jumps", "over", "the",

    "lazy", "dog")) val toChars = Flow[String].mapConcat(_.toList) val print = Sink.foreach(println) def meter[T](name: String): Flow[T, T, NotUsed] = { val msgCounter = Kamon.metrics.counter(name) Flow[T].map { x ⇒ msgCounter.increment(); x } } pangram .via(meter("produced")) .via(toChars) .via(meter("processed")) .runWith(print) AKKA STREAMS - THROUGHPUT
  6. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. EXAMPLE 21 Validation/ Filtering

    Commit Kafka offset valid invalid Store via API
  7. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. Store via API EXAMPLE

    22 Validation/ Filtering Commit Kafka offset valid invalid 100/s 75/s 25/s 75/s
  8. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. Store via API EXAMPLE

    23 Validation/ Filtering Commit Kafka offset valid invalid 100/s 75/s 25/s 75/s 20000/s lag
  9. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. REACTIVE STREAMS 101 25

    Processor Publisher Subscriber request request emit emit
  10. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. REACTIVE STREAMS 101 26

    Processor Publisher Subscriber request request emit emit
  11. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. REACTIVE STREAMS 101 27

    Processor Publisher Subscriber request request emit emit
  12. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. REACTIVE STREAMS 101 28

    Processor Publisher Subscriber request request emit emit
  13. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. REACTIVE STREAMS 101 29

    Processor Publisher Subscriber request request emit emit
  14. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. NO BACKPRESSURE 31 Processor

    Publisher Subscriber request emit ? emit ? request
  15. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. BACKPRESSURE 32 request emit

    request request emit Processor Publisher Subscriber request request emit emit
  16. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  17. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  18. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  19. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  20. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  21. final case class Map[In, Out](f: In ⇒ Out) extends GraphStage[FlowShape[In,

    Out]] { val in = Inlet[In]("Map.in") val out = Outlet[Out]("Map.out") override val shape = FlowShape(in, out) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { override def onPush(): Unit = push(out, f(grab(in))) override def onPull(): Unit = pull(in) setHandlers(in, out, this) } } AKKA STREAMS - GRAPHSTAGE API
  22. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. 40 AKKA STREAMS -

    GRAPHSTAGE onPull onPush onPull onPull onPush
  23. var lastPulled: Long = System.nanoTime() var lastPushed: Long = lastPulled

    private val backpressure = Kamon.histogram(label + "_backpressure") override def onPush(): Unit = { push(out, grab(in)) val now = System.nanoTime() backpressure.record((lastPulled - lastPushed) * 100 / now - lastPushed) lastPushed = now } override def onPull(): Unit = { pull(in) lastPulled = System.nanoTime() } setHandlers(in, out, this) AKKA STREAMS - BACKPRESSURE
  24. var lastPulled: Long = System.nanoTime() var lastPushed: Long = lastPulled

    private val backpressure = Kamon.histogram(label + "_backpressure") override def onPush(): Unit = { push(out, grab(in)) val now = System.nanoTime() backpressure.record((lastPulled - lastPushed) * 100 / now - lastPushed) lastPushed = now } override def onPull(): Unit = { pull(in) lastPulled = System.nanoTime() } setHandlers(in, out, this) AKKA STREAMS - BACKPRESSURE
  25. var lastPulled: Long = System.nanoTime() var lastPushed: Long = lastPulled

    private val backpressure = Kamon.histogram(label + "_backpressure") override def onPush(): Unit = { push(out, grab(in)) val now = System.nanoTime() backpressure.record((lastPulled - lastPushed) * 100 / now - lastPushed) lastPushed = now } override def onPull(): Unit = { pull(in) lastPulled = System.nanoTime() } setHandlers(in, out, this) AKKA STREAMS - BACKPRESSURE
  26. var lastPulled: Long = System.nanoTime() var lastPushed: Long = lastPulled

    private val backpressure = Kamon.histogram(label + "_backpressure") override def onPush(): Unit = { push(out, grab(in)) val now = System.nanoTime() backpressure.record((lastPulled - lastPushed) * 100 / now - lastPushed) lastPushed = now } override def onPull(): Unit = { pull(in) lastPulled = System.nanoTime() } setHandlers(in, out, this) AKKA STREAMS - BACKPRESSURE
  27. val pangram = Source(List("The", "quick", "brown", "fox", "jumps", "over", "the",

    "lazy", "dog")) val toChars = Flow[String].mapConcat(_.toList) val print = Sink.foreach(println) pangram .via(backpressureMeter("produced")) .via(toChars) .via(backpressureMeter("processed")) .runWith(print) AKKA STREAMS - BACKPRESSURE
  28. © 2018 HOMEAWAY. ALL RIGHTS RESERVED. 47 svezfaz/akka-stream-checkpoint CURRENT FEATURES

    ・ throughput ・ backpressure ratio ・ push-pull latency ・ liveness checks FUTURE WORK ・ failures/completion