Konrad 'ktoso' Malawski GeeCON 2014 @ Kraków, PL Konrad `@ktosopl` Malawski (Channeled by @deanwampler) streams Rise of the 1.0 : reactive streams
Konrad 'ktoso' MalawskiGeeCON 2014 @ Kraków, PL
Konrad `@ktosopl` Malawski (Channeled by @deanwampler)
streams
Rise of the 1.0 : reactive streams
Konrad `@ktosopl` Malawski
akka.iotypesafe.comgeecon.org
Java.pl / KrakowScala.plsckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl lambdakrk.pl
(we’re renaming soon!)
@deanwampler [email protected]
(we’re renaming soon!)
Aside…
Streams
A Real Stream…
An Abstract Stream
Streams
“You cannot enter the same river twice”~ Heraclitus
http://en.wikiquote.org/wiki/Heraclitus
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
Reactive Streams 1.0
Reactive Streams
Stream processing
Reactive Streams
Back-pressured
Stream processing
Reactive Streams
Back-pressured Asynchronous
Stream processing
Reactive Streams
Back-pressured Asynchronous
Stream processing Standardised (!)
Reactive Streams: Goals
1. Back-pressured Asynchronous Stream processing
2. Standard implemented by many libraries
Reactive Streams
Reactive Streams - Who?
http://reactive-streams.org
Kaazing Corp.RxJava @ Netflix,
Reactor @ Pivotal (SpringSource),Vert.x @ Red Hat,
Twitter,Akka Streams, Slick @ Typesafe,
Spray @ Spray.io,Oracle,
OpenJDK (Java 9) – Doug Lea - SUNY Oswego …
Reactive Streams - Inter-op
http://reactive-streams.org
We want to make different implementations co-operate with each other.
Reactive Streams - Inter-op
http://reactive-streams.org
The different implementations “talk to each other”using the Reactive Streams protocol.
Reactive Streams - Inter-op
http://reactive-streams.org
The Reactive Streams SPI is NOT meant to be user-api.You should use one of the implementing libraries.
Reactive Streams - Inter-op exampleimport ratpack.rx.RxRatpackimport ratpack.test.embed.EmbeddedAppimport ratpack.handling.Handlerimport ratpack.handling.Contextimport rx.Observableimport scala.collection.JavaConverters._import akka.stream.scaladsl.Flowimport akka.stream.scaladsl.Sourceimport rx.RxReactiveStreamsimport akka.stream.scaladsl.Sinkimport akka.actor.ActorSystemimport akka.stream.FlowMaterializerimport ratpack.http.ResponseChunksimport java.util.function.Consumerimport ratpack.test.http.TestHttpClientimport reactor.rx.Streams
import ratpack.rx.RxRatpackimport ratpack.test.embed.EmbeddedAppimport ratpack.handling.Handlerimport ratpack.handling.Contextimport rx.Observableimport scala.collection.JavaConverters._import akka.stream.scaladsl.Flowimport akka.stream.scaladsl.Sourceimport rx.RxReactiveStreamsimport akka.stream.scaladsl.Sinkimport akka.actor.ActorSystemimport akka.stream.FlowMaterializerimport ratpack.http.ResponseChunksimport java.util.function.Consumerimport ratpack.test.http.TestHttpClientimport reactor.rx.Streams
object ScalaMain extends App {
import akka.actor.ActorSystemimport akka.stream.FlowMaterializerimport ratpack.http.ResponseChunksimport java.util.function.Consumerimport ratpack.test.http.TestHttpClientimport reactor.rx.Streams
object ScalaMain extends App { val system = ActorSystem("InteropTest") implicit val mat = FlowMaterializer()(system) RxRatpack.initialize() val handler = new Handler { override def handle(ctx: Context): Unit ={ // RxJava Observable val intObs = Observable.from((1 to 10).asJava) // Reactive Streams Publisher
import akka.actor.ActorSystemimport akka.stream.FlowMaterializerimport ratpack.http.ResponseChunksimport java.util.function.Consumerimport ratpack.test.http.TestHttpClientimport reactor.rx.Streams
object ScalaMain extends App { val system = ActorSystem("InteropTest") implicit val mat = FlowMaterializer()(system) RxRatpack.initialize() val handler = new Handler { override def handle(ctx: Context): Unit ={ // RxJava Observable val intObs = Observable.from((1 to 10).asJava) // Reactive Streams Publisher
import akka.actor.ActorSystemimport akka.stream.FlowMaterializerimport ratpack.http.ResponseChunksimport java.util.function.Consumerimport ratpack.test.http.TestHttpClientimport reactor.rx.Streams
object ScalaMain extends App { val system = ActorSystem("InteropTest") implicit val mat = FlowMaterializer()(system) RxRatpack.initialize() val handler = new Handler { override def handle(ctx: Context): Unit ={ // RxJava Observable val intObs = Observable.from((1 to 10).asJava) // Reactive Streams Publisher
RxRatpack.initialize() val handler = new Handler { override def handle(ctx: Context): Unit ={ // RxJava Observable val intObs = Observable.from((1 to 10).asJava) // Reactive Streams Publisher val intPub = RxReactiveStreams.toPublisher(intObs) // Akka Streams Source val stringSource = Source(intPub).map(_.toString) // Reactive Streams Publisher val stringPub = stringSource.runWith( Sink.fanoutPublisher(1, 1)) // Reactor Stream
// Reactive Streams Publisher val intPub = RxReactiveStreams.toPublisher(intObs) // Akka Streams Source val stringSource = Source(intPub).map(_.toString) // Reactive Streams Publisher val stringPub = stringSource.runWith( Sink.fanoutPublisher(1, 1)) // Reactor Stream val reactor.function.Function val linesStream = Streams.create(stringPub).map[String]( new Function[String, String] { override def apply(in: String) = in+"\n" }) // and now render the HTTP response
val stringPub = stringSource.runWith( Sink.fanoutPublisher(1, 1)) // Reactor Stream val reactor.function.Function val linesStream = Streams.create(stringPub).map[String]( new Function[String, String] { override def apply(in: String) = in+"\n" }) // and now render the HTTP response ctx.render( ResponseChunks.stringChunks(linesStream)) } })
EmbeddedApp.fromHandler(handler).test( new Consumer[TestHttpClient] {
// and now render the HTTP response ctx.render( ResponseChunks.stringChunks(linesStream)) } })
EmbeddedApp.fromHandler(handler).test( new Consumer[TestHttpClient] { override def accept( client: TestHttpClient): Unit = { val text = client.getText() println(text) system.shutdown() } })}
// and now render the HTTP response ctx.render( ResponseChunks.stringChunks(linesStream)) } })
EmbeddedApp.fromHandler(handler).test( new Consumer[TestHttpClient] { override def accept( client: TestHttpClient): Unit = { val text = client.getText() println(text) system.shutdown() } })}
Reactive Streams - Inter-op
public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete();}
public interface Publisher<T> { public void subscribe(Subscriber<? super T> s);}
public interface Subscription { public void request(long n); public void cancel();}
What is back-pressure?
Back-pressure? Example Without
Publisher[T] Subscriber[T]
Back-pressure? Example Without
Fast Publisher Slow Subscriber
Back-pressure? “Why would I need that!?”
Back-pressure? Push + NACK model
Back-pressure? Push + NACK model
Subscriber usually has some kind of buffer.
Back-pressure? Push + NACK model
Back-pressure? Push + NACK model
Back-pressure? Push + NACK model
What if the buffer overflows?
Back-pressure? Push + NACK model (a)
Use bounded buffer, drop messages + require re-sending
Back-pressure? Push + NACK model (a)
Kernel does this!Routers do this!
(TCP)
Use bounded buffer, drop messages + require re-sending
Back-pressure? Push + NACK model (b)Increase buffer size… Well, while you have memory available!
Back-pressure? Push + NACK model (b)
NACKing is NOT enough!
Negative ACKnowledgement
Back-pressure? Example NACKing
Buffer overflow is imminent!
Back-pressure? Example NACKingTelling the Publisher to slow down / stop sending…
Back-pressure? Example NACKing
NACK did not make it in time, because M was in-flight!
Back-pressure?
speed(publisher) < speed(subscriber)
Back-pressure? Fast Subscriber, No Problem
No problem!
Back-pressure? Reactive-Streams
= “Dynamic Push/Pull”
Just push – not safe when Slow Subscriber
Just pull – too slow when Fast Subscriber
Back-pressure? RS: Dynamic Push/Pull
Solution: Dynamic adjustment
Back-pressure? RS: Dynamic Push/Pull
Just push – not safe when Slow Subscriber
Just pull – too slow when Fast Subscriber
Back-pressure? RS: Dynamic Push/PullSlow Subscriber sees it’s buffer can take 3 elements. Publisher will never blow up it’s buffer.
Back-pressure? RS: Dynamic Push/PullFast Publisher will send at-most 3 elements. This is pull-based-backpressure.
Back-pressure? RS: Dynamic Push/Pull
Fast Subscriber can issue more Request(n), before more data arrives!
Back-pressure? RS: Dynamic Push/PullFast Subscriber can issue more Request(n), before more data arrives.
Publisher can accumulate demand.
Back-pressure? RS: Accumulate demand
Publisher accumulates total demand per subscriber.
Back-pressure? RS: Accumulate demandTotal demand of elements is safe to publish. Subscriber’s buffer will not overflow.
Back-pressure? RS: Requesting “a lot”
Fast Subscriber can issue arbitrary large requests, including “gimme all you got” (Long.MaxValue)
streams
Akka
• Fault tolerant
• Supervision hierarchies
• Failure detection
• Asynchronous data processing
• Optimised for high performance
• both in-jvm and across network
• Adaptive Cluster
• Load balancing among Cluster Nodes
Actor 131 Actor 132
Supervisor 1
Actor 12 Actor 13
Actor 111 Actor 112
Supervisor 11
Akka
Akka has multiple modules:
akka-actor: actors (concurrency abstraction)akka-camel: integrationakka-remote: remote actorsakka-cluster: clusteringakka-persistence: CQRS / Event Sourcingakka-streams: stream processing…
AkkaAkka 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
AkkaAkka is a high-performance concurrency library for Scala and Java.
At it’s core it focuses on the Actor Model:
class Player extends Actor {
def receive = { case NextTurn => sender() ! decideOnMove() }
def decideOnMove(): Move = ???}
Akka Actors
Akka
Actors are: a distribution and concurrency abstraction.
Streams are: a way to describe how data flows through a system.
Akka Streams – Linear Flow
Akka Streams – Linear Flow
Akka Streams – Linear Flow
Akka Streams – Linear Flow
Akka Streams – Linear Flow
Flow[Double].map(_.toInt). [...]
No Source attached yet.“Pipe ready to work with Doubles”.
Akka Streams – Linear Flow
implicit val sys = ActorSystem()implicit val mat = ActorMaterializer()
Source(1 to 3).runWith(Sink.foreach(println))
Akka Streams – Linear Flow
Source(1 to 3).runWith(Sink.foreach(println))
// sugar for runWithSource(1 to 3).foreach(println)
implicit val sys = ActorSystem()implicit val mat = ActorMaterializer()
Akka Streams – Linear Flow
Source(1 to 3).runWith(Sink.foreach(println))
// sugar for runWithSource(1 to 3).foreach(println)
Sink.foldSink.headSink.ignoreSink.publisherSink.cancelled// your own Sink…
implicit val sys = ActorSystem()implicit val mat = ActorMaterializer()
Akka Streams – Flows are reusable
sink.runWith(Source(1 to 10)) sink.runWith(Source(1 to 100)) sink.runWith(Source(1 to 1000))
source.runWith(Sink.ignore) source.runWith(Sink.foreach(println))
Multiple materializations
val ready = source.to(Sink.ignore)
ready.run()ready.run()
Akka Streams <-> Actors – Advancedval subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”))
Source(1 to 100) .map(_.toString) .filter(_.length == 2) .drop(2) .conflate(seed => seed)((acc, i) => acc + i) .groupBy(_.last) .runWith(subscriber)
All the usual ops available for Linear Flows.
Akka Streams <-> Actors – Advancedval subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”))
Source(1 to 100) .map(_.toString) .filter(_.length == 2) .drop(2) .conflate(seed => seed)((acc, i) => acc + i) .groupBy(_.last) .runWith(subscriber)
Aggregating values until downstream demand comes.
Akka Streams <-> Actors – Advancedval subscriber = ActorSubscriber( system.actorOf(Props[SubStreamParent], ”parent”))
Source(1 to 100) .map(_.toString) .filter(_.length == 2) .drop(2) .conflate(seed => seed)((acc, i) => acc + i) .groupBy(_.last) .runWith(subscriber)
Creates a stream of streams:Source[(Int, Source[String])]
Akka Streams: Graphs
val p: Publisher[String] = FlowGraph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(new StrictRoundRobin[String])
in1 ~> merge.in(0) in2 ~> merge.in(1) merge.out ~> o.inlet }.run()
val in1 = Source(List("a", "b", "c", "d")) val in2 = Source(List("e", "f"))
val out = Sink.publisher[String]
Akka Streams: Graphs
val p: Publisher[String] = FlowGraph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(new StrictRoundRobin[String])
in1 ~> merge.in(0) in2 ~> merge.in(1) merge.out ~> o.inlet }.run()
val in1 = Source(List("a", "b", "c", "d")) val in2 = Source(List("e", "f"))
val out = Sink.publisher[String]
Sink[String, Publisher[String]]
imports Graphs
Akka Streams: Graphs
val p: Publisher[String] = FlowGraph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(new StrictRoundRobin[String])
in1 ~> merge.in(0) in2 ~> merge.in(1) merge.out ~> o.inlet }.run()
val in1 = Source(List("a", "b", "c", "d")) val in2 = Source(List("e", "f"))
val out = Sink.publisher[String]
Sink[String, Publisher[String]]
materializes a Publisher[String]
Reactive Streams
Bigger than Scala-ecosystem - JDK-wide (and wider).
Inter-operable back-pressure protocol.
Future work: reactive-streams-io, reactive-streams-js
Akka Streams - one of the leading Reactive Streams impls. Complex in-memory stream processing.
SlickR - provides Reactive Stream from DB queries
Akka Http - Akka Streams based; “Spray 2.0”
Wrapping up
Available Sources
•FutureSource•IterableSource•IteratorSource•PublisherSource•SubscriberSource•ThunkSource•SynchronousFileSource•TickSource (timer based)•… easy to add your own!
Available operations• drop / dropWithin• take / takeWithin• filter• groupBy• grouped• transform• buffer• collect• expand• splitWhen / splitAfter• map• scan• prefixAndTail• … easy to add your own!
“Rate – detaching” operations:• buffer• collect• concat• conflate
Available Sinks• BlackHoleSink• FoldSink• ForeachSink• FutureSink• OnCompleteSink• UdpSink [next release]• SynchronousFileSink• PublisherSink / FanoutPublisherSink• SubscriberSink• FileSink• … easy to add your own!
Available Junctions• Broadcast• Merge
• FlexiMerge• Route
• FlexiRoute• Zip
• ZipWith• Unzip
• UnzipWith• Concat• … easy to add your own!
Community ecosystem• Kafka • Spark (soon?)• Slick • S3• Files• MongoDB• …
Links• The projects: • http://akka.io • http://reactive-streams.org
• Akka User - mailing list: • https://groups.google.com/group/akka-user
Tänan! Dzięki! Thanks!
ありがとう!
ktoso @ typesafe.com t: ktosopl / g: ktoso blog: project13.pl
©Typesafe 2015 – All Rights Reserved
The company soon to be
previously known as