Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Reactive Streams / Akka Streams - GeeCON Prague 2014

4,902 views

Published on

Talk explaining the core concepts behind reactive streams.

Published in: Technology

Reactive Streams / Akka Streams - GeeCON Prague 2014

  1. 1. Konrad 'ktoso' Malawski GeeCON 2014 @ Kraków, PL Akka Streams Konrad `@ktosopl` Malawski OST: Tomas Dvorak ./‘ ./‘
  2. 2. hAkker @ Konrad `@ktosopl` Malawski Akka Team, Reactive Streams TCK
  3. 3. hAkker @ Konrad `@ktosopl` Malawski typesafe.com geecon.org Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
  4. 4. You? ?
  5. 5. Czech User Group? You?
  6. 6. You? ? z ?
  7. 7. You? ? z ? ?
  8. 8. You? ? z ? ? ?
  9. 9. Streams
  10. 10. Streams
  11. 11. Streams “You cannot enter the same river twice” ~ Heraclitus http://en.wikiquote.org/wiki/Heraclitus
  12. 12. Streams Real Time Stream Processing When you attach “late” to a Publisher, you may miss initial elements – it’s a river of data. http://en.wikiquote.org/wiki/Heraclitus
  13. 13. Reactive Streams
  14. 14. Reactive Streams Stream processing
  15. 15. Reactive Streams Back-pressured Stream processing
  16. 16. Reactive Streams Back-pressured Asynchronous Stream processing
  17. 17. Reactive Streams Back-pressured Asynchronous Stream processing Standardised (!)
  18. 18. Reactive Streams: Goals 1. Back-pressured Asynchronous Stream processing 2. Standard implemented by many libraries
  19. 19. Reactive Streams - Specification & TCK http://reactive-streams.org
  20. 20. Reactive Streams - Who? Kaazing Corp. rxJava @ Netflix, reactor @ Pivotal (SpringSource), vert.x @ Red Hat, Twitter, akka-streams @ Typesafe, spray @ Spray.io, Oracle, java (?) – Doug Lea - SUNY Oswego … http://reactive-streams.org
  21. 21. Reactive Streams - Inter-op We want to make different implementations co-operate with each other. http://reactive-streams.org
  22. 22. Reactive Streams - Inter-op The different implementations “talk to each other” using the Reactive Streams protocol. http://reactive-streams.org
  23. 23. Reactive Streams - Inter-op The Reactive Streams SPI is NOT meant to be user-api. You should use one of the implementing libraries. http://reactive-streams.org
  24. 24. What is back-pressure?
  25. 25. Back-pressure? Example Without Publisher[T] Subscriber[T]
  26. 26. Back-pressure? Example Without Fast Publisher Slow Subscriber
  27. 27. Back-pressure? “Why would I need that!?”
  28. 28. Back-pressure? Push + NACK model
  29. 29. Back-pressure? Push + NACK model Subscriber usually has some kind of buffer.
  30. 30. Back-pressure? Push + NACK model
  31. 31. Back-pressure? Push + NACK model
  32. 32. Back-pressure? Push + NACK model What if the buffer overflows?
  33. 33. Back-pressure? Push + NACK model (a) Use bounded buffer, drop messages + require re-sending
  34. 34. Back-pressure? Push + NACK model (a) Use bounded buffer, drop messages + require re-sending Kernel does this! Routers do this! (TCP)
  35. 35. Back-pressure? Push + NACK model (b) Increase buffer size… Well, while you have memory available!
  36. 36. Back-pressure? Push + NACK model (b)
  37. 37. Back-pressure? NACKing is NOT enough.
  38. 38. Negative ACKnowledgement
  39. 39. Back-pressure? Example NACKing Buffer overflow is imminent!
  40. 40. Back-pressure? Example NACKing Telling the Publisher to slow down / stop sending…
  41. 41. Back-pressure? Example NACKing NACK did not make it in time, because M was in-flight!
  42. 42. Back-pressure? speed(publisher) < speed(subscriber)
  43. 43. Back-pressure? Fast Subscriber, No Problem No problem!
  44. 44. Back-pressure? Reactive-Streams = “Dynamic Push/Pull”
  45. 45. Back-pressure? RS: Dynamic Push/Pull Just push – not safe when Slow Subscriber Just pull – too slow when Fast Subscriber
  46. 46. Back-pressure? RS: Dynamic Push/Pull Just push – not safe when Slow Subscriber Just pull – too slow when Fast Subscriber Solution: Dynamic adjustment
  47. 47. Back-pressure? RS: Dynamic Push/Pull Slow Subscriber sees it’s buffer can take 3 elements. Publisher will never blow up it’s buffer.
  48. 48. Back-pressure? RS: Dynamic Push/Pull Fast Publisher will send at-most 3 elements. This is pull-based-backpressure.
  49. 49. Back-pressure? RS: Dynamic Push/Pull Fast Subscriber can issue more Request(n), before more data arrives!
  50. 50. Back-pressure? RS: Dynamic Push/Pull Fast Subscriber can issue more Request(n), before more data arrives. Publisher can accumulate demand.
  51. 51. Back-pressure? RS: Accumulate demand Publisher accumulates total demand per subscriber.
  52. 52. Back-pressure? RS: Accumulate demand Total demand of elements is safe to publish. Subscriber’s buffer will not overflow.
  53. 53. Back-pressure? RS: Requesting “a lot” Fast Subscriber can issue arbitrary large requests, including “gimme all you got” (Long.MaxValue)
  54. 54. Back-pressure? RS: Dynamic Push/Pull MAX speed
  55. 55. Back-pressure? RS: Dynamic Push/Pull Easy MAX speed
  56. 56. How does fit all this?
  57. 57. Akka Akka has multiple modules: akka-actor: actors (concurrency abstraction) akka-camel: integration akka-remote: remote actors akka-cluster: clustering akka-persistence: CQRS / Event Sourcing akka-streams: stream processing …
  58. 58. Akka Akka is a high-performance concurrency library for Scala and Java. At it’s core it focuses on the Actor Model:
  59. 59. Akka Akka is a high-performance concurrency library for Scala and Java. At it’s core it focuses on the Actor Model: An Actor can only: • Send and receive messages • Create Actors • Change it’s behaviour
  60. 60. Akka class Player extends Actor { def receive = { case NextTurn => sender() ! decideOnMove() } def decideOnMove(): Move = ??? }
  61. 61. Akka Akka has multiple modules: akka-actor: actors (concurrency abstraction) akka-camel: integration akka-remote: remote actors akka-cluster: clustering akka-persistence: CQRS / Event Sourcing akka-streams: stream processing …
  62. 62. Akka Streams 0.9 early preview
  63. 63. Akka Streams – Linear Flow
  64. 64. Akka Streams – Linear Flow
  65. 65. Akka Streams – Linear Flow
  66. 66. Akka Streams – Linear Flow
  67. 67. Akka Streams – Linear Flow Flow[Double].map(_.toInt). [...] No Source attached yet. “Pipe ready to work with Doubles”.
  68. 68. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") An ActorSystem is the world in which Actors live in. AkkaStreams uses Actors, so it needs ActorSystem.
  69. 69. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() Contains logic on HOW to materialise the stream.
  70. 70. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() A materialiser chooses HOW to materialise a Stream. The Flow’s AST is fully “lifted”. The Materialiser can choose to materialise the Flow in any way it sees fit. Our implementation uses Actors. But you could easily plug in an SparkMaterializer!
  71. 71. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() You can configure it’s buffer sizes etc.
  72. 72. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() val foreachSink = Sink.foreach[Int](println) val mf = Source(1 to 3).runWith(foreachSink)
  73. 73. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() val foreachSink = Sink.foreach[Int](println) val mf = FlowFrom(1 to 3).runWith(foreachSink)(mat) Uses the implicit FlowMaterializer
  74. 74. Akka Streams – Linear Flow implicit val sys = ActorSystem("tokyo-sys") implicit val mat = FlowMaterializer() // sugar for runWith Source(1 to 3).foreach(println)
  75. 75. Akka Streams – Linear Flow val mf = Flow[Int]. map(_ * 2). runWith(Sink.foreach(println)) // is missing a Source, // can NOT run == won’t compile!
  76. 76. Akka Streams – Linear Flow val f = Flow[Int]. map(_ * 2). runWith(Sink.foreach(i => println(s"i = $i”))). // needs Source to run!
  77. 77. Akka Streams – Linear Flow val f = Flow[Int]. map(_ * 2). runWith(Sink.foreach(i => println(s"i = $i”))). // needs Source to run!
  78. 78. Akka Streams – Linear Flow val f = Flow[Int]. map(_ * 2). runWith(Sink.foreach(i => println(s"i = $i”))). // needs Source to run!
  79. 79. Akka Streams – Linear Flow val f = Flow[Int]. map(_ * 2). runWith(Sink.foreach(i => println(s"i = $i”))). // needs Source to run! f.connect(Source(1 to 10)).run()
  80. 80. Akka Streams – Linear Flow val f = Flow[Int]. map(_ * 2). runWith(Sink.foreach(i => println(s"i = $i”))). // needs Source to run! f.connect(Source(1 to 10)).run() With a Source attached… it can run()
  81. 81. Akka Streams – Linear Flow Flow[Int]. map(_.toString). runWith(Source(1 to 10), Sink.ignore) Connects Source and Sink, then runs
  82. 82. Akka Streams – Flows are reusable f.withSource(IterableSource(1 to 10)).run() f.withSource(IterableSource(1 to 100)).run() f.withSource(IterableSource(1 to 1000)).run()
  83. 83. Akka Streams <-> Actors – Advanced val subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”)) Source(1 to 100). map(_.toString). filter(_.length == 2). drop(2). groupBy(_.last). runWith(subscriber)
  84. 84. Akka Streams <-> Actors – Advanced val subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”)) Source(1 to 100). map(_.toString). filter(_.length == 2). drop(2). groupBy(_.last). runWith(subscriber) Each “group” is a stream too! It’s a “Stream of Streams”.
  85. 85. Akka Streams <-> Actors – Advanced groupBy(_.last). GroupBy groups “11” to group “1”, “12” to group “2” etc.
  86. 86. Akka Streams <-> Actors – Advanced groupBy(_.last). Source It offers (groupKey, subStreamSource) to Subscriber
  87. 87. Akka Streams <-> Actors – Advanced groupBy(_.last). Source It can then start children, to handle the sub-flows!
  88. 88. Akka Streams <-> Actors – Advanced groupBy(_.last). Source For example, one child for each group.
  89. 89. Akka Streams <-> Actors – Advanced val subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”)) Source(1 to 100). map(_.toString). filter(_.length == 2). drop(2). groupBy(_.last). runWith(subscriber) The Actor, will consume SubStream offers.
  90. 90. Consuming streams with Actors (advanced)
  91. 91. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber with ImplicitFlowMaterializer with ActorLogging { override def requestStrategy = OneByOneRequestStrategy override def receive = { case OnNext((groupId: String, subStream: Source[String])) => val subSub = context.actorOf(Props[SubStreamSubscriber], s"sub-$groupId") subStream.runWith(Sink.subscriber(ActorSubscriber(subSub))) } }
  92. 92. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber with ImplicitFlowMaterializer with ActorLogging { override def requestStrategy = OneByOneRequestStrategy override def receive = { case OnNext((groupId: String, subStream: Source[String])) => val subSub = context.actorOf(Props[SubStreamSubscriber], s"sub-$groupId") subStream.runWith(Sink.subscriber(ActorSubscriber(subSub))) } }
  93. 93. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber with ImplicitFlowMaterializer with ActorLogging { override def requestStrategy = OneByOneRequestStrategy override def receive = { case OnNext((groupId: String, subStream: Source[String])) => val subSub = context.actorOf(Props[SubStreamSubscriber], s"sub-$groupId") subStream.runWith(Sink.subscriber(ActorSubscriber(subSub))) } }
  94. 94. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber with ImplicitFlowMaterializer with ActorLogging { override def requestStrategy = OneByOneRequestStrategy override def receive = { case OnNext((groupId: String, subStream: Source[String])) => val subSub = context.actorOf(Props[SubStreamSubscriber], s"sub-$groupId") subStream.runWith(Sink.subscriber(ActorSubscriber(subSub))) } }
  95. 95. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber with ImplicitFlowMaterializer with ActorLogging { override def requestStrategy = OneByOneRequestStrategy override def receive = { case OnNext((groupId: String, subStream: Source[String])) => val subSub = context.actorOf(Props[SubStreamSubscriber], s"sub-$groupId") subStream.runWith(Sink.subscriber(ActorSubscriber(subSub))) } }
  96. 96. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber { override def requestStrategy = WatermarkRequestStrategy(highWatermark = 10) override def receive = { case OnNext(n: String) => println(s”n = $n”) } }
  97. 97. Akka Streams <-> Actors – Advanced class SubStreamParent extends ActorSubscriber { override def requestStrategy = WatermarkRequestStrategy(highWatermark = 10) override def receive = { case OnNext(n: String) => println(s”n = $n”) } }
  98. 98. Akka Streams – FlowGraph FlowGraph
  99. 99. Akka Streams – FlowGraph Linear Flows or non-akka pipelines Could be another RS implementation!
  100. 100. Akka Streams – GraphFlow Fan-out elements and Fan-in elements
  101. 101. Akka Streams – GraphFlow // first define some pipeline pieces val f1 = Flow[Input].map(_.toIntermediate) val f2 = Flow[Intermediate].map(_.enrich) val f3 = Flow[Enriched].filter(_.isImportant) val f4 = Flow[Intermediate].mapFuture(_.enrichAsync) // then add input and output placeholders val in = SubscriberSource[Input] val out = PublisherSink[Enriched]
  102. 102. Akka Streams – GraphFlow
  103. 103. Akka Streams – GraphFlow val b3 = Broadcast[Int]("b3") val b7 = Broadcast[Int]("b7") val b11 = Broadcast[Int]("b11") val m8 = Merge[Int]("m8") val m9 = Merge[Int]("m9") val m10 = Merge[Int]("m10") val m11 = Merge[Int]("m11") val in3 = Source(List(3)) val in5 = Source(List(5)) val in7 = Source(List(7))
  104. 104. Akka Streams – GraphFlow
  105. 105. Akka Streams – GraphFlow // First layer in7 ~> b7 b7 ~> m11 b7 ~> m8 in5 ~> m11 in3 ~> b3 b3 ~> m8 b3 ~> m10
  106. 106. Akka Streams – GraphFlow // Second layer m11 ~> b11 b11 ~> Flow[Int].grouped(1000) ~> resultFuture2 b11 ~> m9 b11 ~> m10 m8 ~> m9
  107. 107. Akka Streams – GraphFlow // Third layer m9 ~> Flow[Int].grouped(1000) ~> resultFuture9 m10 ~> Flow[Int].grouped(1000) ~> resultFuture10
  108. 108. Akka Streams – GraphFlow // Third layer m9 ~> Flow[Int].grouped(1000) ~> resultFuture9 m10 ~> Flow[Int].grouped(1000) ~> resultFuture10
  109. 109. Akka Streams – GraphFlow // Third layer m9 ~> Flow[Int].grouped(1000) ~> resultFuture9 m10 ~> Flow[Int].grouped(1000) ~> resultFuture10
  110. 110. Akka Streams – GraphFlow Sinks and Sources are “keys” which can be addressed within the graph val resultFuture2 = Sink.future[Seq[Int]] val resultFuture9 = Sink.future[Seq[Int]] val resultFuture10 = Sink.future[Seq[Int]] val g = FlowGraph { implicit b => // ... m10 ~> Flow[Int].grouped(1000) ~> resultFuture10 // ... }.run() Await.result(g.get(resultFuture2), 3.seconds).sorted should be(List(5, 7))
  111. 111. Akka Streams – GraphFlow Sinks and Sources are “keys” which can be addressed within the graph val resultFuture2 = Sink.future[Seq[Int]] val resultFuture9 = Sink.future[Seq[Int]] val resultFuture10 = Sink.future[Seq[Int]] val g = FlowGraph { implicit b => // ... m10 ~> Flow[Int].grouped(1000) ~> resultFuture10 // ... }.run() Await.result(g.get(resultFuture2), 3.seconds).sorted should be(List(5, 7))
  112. 112. Akka Streams – GraphFlow val g = FlowGraph {} FlowGraph is immutable and safe to share and re-use!
  113. 113. Available Elements 0.9 early preview
  114. 114. Available Sources • FutureSource • IterableSource • IteratorSource • PublisherSource • SubscriberSource • ThunkSource • TickSource (timer based) • … easy to add your own! 0.9 early preview
  115. 115. Available operations • drop / dropWithin • take / takeWithin • filter • groupBy • grouped • map • prefixAndTail • … easy to add your own! “Rate – detaching” operations: • buffer • collect • concat • conflate 0.9 early preview
  116. 116. Available Sinks • BlackHoleSink • FoldSink • ForeachSink • FutureSink • OnCompleteSink • PublisherSink / FanoutPublisherSink • SubscriberSink • … easy to add your own! 0.9 early preview
  117. 117. Available Junctions • Broadcast • Merge • FlexiMerge • Zip • Unzip • Concat • … easy to add your own! 0.9 early preview
  118. 118. Akka-Streams Coming soon!
  119. 119. Rough plans • 0.9 released • 0.10 in 2~3 weeks • 1.0 “soon” after… • Means “stabilised APIs” • Will not yet be performance tuned, though it’s already pretty good… we know where and how we can tune it for 1.x. 0.7 early preview
  120. 120. Java DSL • Partial Java DSL in 0.9 (released) • Full Java DSL in 0.10 (in 2~3 weeks) • as 1st class citizen (!) 0.7 early preview
  121. 121. Spray => Akka-Http && ReactiveStreams Spray is now merged into Akka, as Akka-Http Works on Reactive Streams Streaming end-to-end!
  122. 122. Links • http://akka.io • http://reactive-streams.org • https://groups.google.com/group/akka-user • 0.7 release http://akka.io/news/2014/09/12/akka-streams-0.7-released.html • javadsl https://github.com/akka/akka/pulls?q=is%3Apr+javadsl
  123. 123. Děkuji vám! Dzięki! ありがとう! Ask questions, get Stickers! (for real!) http://akka.io ktoso @ typesafe.com twitter: ktosopl github: ktoso team blog: letitcrash.com
  124. 124. ©Typesafe 2014 – All Rights Reserved

×