Практика Akka Streams
Алексей Романчук
2
Обо мне
3
4
Обо мне
• Знаете синтаксис Scala
• Знакомы со стандартной библиотекой Scala
• Слышали про Reactive Streams
• Видели Akka Streams*
5
О вас
В предыдущих сериях
T
T
Iterable[T]
T
Iterable[T]
Future[T]
T
Iterable[T]
Future[T]
Source[T]
• Реализуют спецификацию Reactive Streams
• Основаны на Akka
• Умеют Backpressure
• Статически типизированы
8
Akka Streams
9
Ключевые абстракции
Source SinkFlow
Базовые комбинаторы
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
11
Source
Source.single(1)

Source.failed(new Exception())

Source.empty[Int]



Source.repeat(1)

Source.fromIterator(() => Iterator(1, 2, 3))

Source.cycle(() => Iterator(1, 2, 3))



Source.tick(1 second, 100 millis, ())
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
12
Sink
Sink.ignore

Sink.cancelled

Sink.foreach[Int](println)



Sink.head

Sink.headOption

Sink.last

Sink.lastOption



Sink.fold(0)(_ + _)

Sink.reduce[Int](_ + _)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
13
Flow
Flow[Int].map(identity)

Flow[Int].mapAsync(10)(Future.successful)

Flow[Int].mapAsyncUnordered(10)(Future.successful)



Flow[Int].filter(_ % 2 == 0)

Flow[Int].collect {

case n if n % 2 == 0 => n

}
Flow[Int].take(10)

Flow[Int].takeWhile(_ > 10)

Flow[Int].drop(10)

Flow[Int].dropWhile(_ > 10)
14
Flow
Flow[Int].flatMapConcat(n =>

Source.fromIterator(() => Iterator.range(1, n))

)
1 2 3 Х
14
Flow
Flow[Int].flatMapConcat(n =>

Source.fromIterator(() => Iterator.range(1, n))

)
1 2 3 Х
1 Х
14
Flow
Flow[Int].flatMapConcat(n =>

Source.fromIterator(() => Iterator.range(1, n))

)
1 2 3 Х
1 Х 1 2 Х
14
Flow
Flow[Int].flatMapConcat(n =>

Source.fromIterator(() => Iterator.range(1, n))

)
1 2 3 Х
1 Х 1 2 Х 1 2 3 Х
14
Flow
Flow[Int].flatMapConcat(n =>

Source.fromIterator(() => Iterator.range(1, n))

)
1 2 3 Х
1 Х 1 2 Х 1 2 3 Х
1 1 2 1 2 3 Х
Пример №1
• Прочитать с хадупа логи транзакций и найти там 100
примеров фродных транзакций
16
Задача
List Files APIRead Line Result
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
17
Наивная реализация
def checkFraud(in: String): Future[Option[TransactionId]]
= ???
val result: List[TransactionId] = (for {

file <- hdfs.listStatusIterator(path)

if file.isFile

line <-fromInputStream(fs.open(file.getPath)).getLines()

resultOpt = Await.result(checkFraud(line), 10 seconds)

result <- resultOpt

} yield result)
.take(100).toList
Ок?
• Не терять лишнего времени на скачивание
• Параллельные запросы к сервису
• Не превышать ограничения в 10 запросов в секунду
19
Требования
20
Красивая картинка со схемой
List Files APIRead Line Result
21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

21
Красивый код на стримах
val stream =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

Все на стандартных
комбинаторах
23
Запуск
val result: Future[List[TransactionId]] = stream.run()


val result2: Future[List[TransactionId]] = stream.run()
24
Запуск
List Files APIRead Line Result
24
Запуск
List Files APIRead Line Result
run()
Future Stream
24
Запуск
List Files APIRead Line Result
run()
Future Stream
run()
Future Stream
Материализация
Чертеж → Деталь
27
RunnableGraph
RunnableGraph[+Mat] {
def run(): Mat
}
28
Mat
final class Source[+Out, +Mat]
final class Flow[-In, +Out, +Mat]
final class Sink[-In, +Mat]
29
Sources
case object NotUsed
def single[T](element: T)
def empty[T]
def failed[T](cause: Throwable)
def repeat[T](element: T)
def fromIterator[T](f: () => Iterator[T])
def cycle[T](f: () => Iterator[T])
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
30
Sinks
def ignore: Sink[Any, Future[Done]]
def foreach[T](f: T => Unit): Sink[T, Future[Done]]
def head[T]: Sink[T, Future[T]]
def headOption[T]: Sink[T, Future[Option[T]]]
def fold[U, T](z: U)(f: (U, T) => U): Sink[T, Future[U]]
def reduce[T](f: (T, T) => T): Sink[T, Future[T]]
31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

31
Композиция
val source: Source[Int, NotUsed] = Source.single(1)

val sink: Sink[Any, Future[Done]] = Sink.ignore



val s1: RunnableGraph[NotUsed] = source.to(sink)



val s2: RunnableGraph[Future[Done]] =

source.toMat(sink)(Keep.right)



val s3: RunnableGraph[(NotUsed, Future[Done])] =

source.toMat(sink)(Keep.both)

32
Композиция
33
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =
Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

33
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =
Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

33
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =
Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

33
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =
Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

34
Красивая картинка со схемой
List Files APIRead Line Result
run()
Stream
run()
Future StreamFuture
34
Красивая картинка со схемой
List Files APIRead Line Result
run()
Future
Stream
run()
Future StreamFuture
Пример №2
• Читать данные из очереди и обрабатывать.
36
Задача
Source Process Sink
Future
37
Красивый код на стримах
val source = ZMQSource(context,

mode = ZMQ.PULL,

timeout = 1 second,

addresses = List("tcp: //127.0.0.1:12345")

)



def process(in: ByteString): Future[Int] = ???
37
Красивый код на стримах
val source = ZMQSource(context,

mode = ZMQ.PULL,

timeout = 1 second,

addresses = List("tcp: //127.0.0.1:12345")

)



def process(in: ByteString): Future[Int] = ???
37
Красивый код на стримах
val source = ZMQSource(context,

mode = ZMQ.PULL,

timeout = 1 second,

addresses = List("tcp: //127.0.0.1:12345")

)



def process(in: ByteString): Future[Int] = ???
37
Красивый код на стримах
val source = ZMQSource(context,

mode = ZMQ.PULL,

timeout = 1 second,

addresses = List("tcp: //127.0.0.1:12345")

)



def process(in: ByteString): Future[Int] = ???
38
Красивый код на стримах
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

38
Красивый код на стримах
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

38
Красивый код на стримах
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

38
Красивый код на стримах
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

38
Красивый код на стримах
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

39
Красивая картинка со схемой
Source Process Sink
Future
39
Красивая картинка со схемой
Source Process Sink
Future
StreamFuture
Как это остановить?
41
Проблема остановки
val stream = source

.mapAsync(10)(process)

.toMat(Sink.last)(Keep.right)



val result: Future[Int] = stream.run()

result.onComplete(_ => "We are done here")

42
Проблема остановки
Source Process Sink
Future
42
Проблема остановки
Source Process Sink
FutureControl
42
Проблема остановки
Source Process Sink
Future
StreamControl
Control
Future
43
Проблема остановки
trait Control {

/**

* Disconnect the underlying ZMQ socket, deliver the
remaining data and finally close the socket.

*/

def gracefulStop(): Unit

}
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
44
Проблема остановки
val source: Source[ByteString, Control] = ???
val stream: RunnableGraph[(Control, Future[Int])] =
source

.mapAsync(10)(process)

.toMat(Sink.head)(Keep.both)



val (control: Control, result: Future[Int]) = stream.run()

result.onComplete(_ => "We are done here")

control.gracefulStop()
Пример №3
46
Механика
Flow[T]
46
Механика
Flow[T]
Request
Cancel
46
Механика
Flow[T]
Request Request
Cancel Cancel
46
Механика
Flow[T]
Request Request
T
Error
Complete
Cancel Cancel
46
Механика
Flow[T]
T
Error
Complete
Request Request
T
Error
Complete
Cancel Cancel
• Emit элемента
• Завершение
• Ошибка
• Backpressure
• Отмена
47
Что нужно знать про stages
48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

48
Свой Source
val s: Source[Int, ActorRef] =

Source.actorPublisher[Int](Props(new MySourceActor))



class MySourceActor extends ActorPublisher[Int] {

override def receive: Receive = {

case ActorPublisherMessage.Request(n) =>

onNext(100)

onCompleteThenStop()

onErrorThenStop(new Exception)

case ActorPublisherMessage.Cancel =>

context.stop(self)

}

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

49
Reactive Zmq
case object GracefulStop

case object DeliverMore

class MySourceActor(addr: String)
extends ActorPublisher[Array[Byte]] {

var conn: ZMQ.Socket = ???



override def preStart(): Unit = {

conn.connect(addr)

}



override def postStop(): Unit = {

conn.disconnect(addr)

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

50
Reactive Zmq
override def receive: Receive = {

case ActorPublisherMessage.Request(_) | DeliverMore =>

Option(conn.recv()) match {

case Some(msg) =>

onNext(msg)

if(totalDemand > 0) self ! DeliverMore

case None => self ! DeliverMore

}

case ActorPublisherMessage.Cancel | GracefulStop =>

conn.disconnect(addr)

onCompleteThenStop()

}

51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
51
Reactive Zmq
val source: Source[Array[Byte], Control] =

Source.actorPublisher[Array[Byte]](

Props(new MySourceActor( ???))

).mapMaterializedValue { ref =>

new Control {

override def gracefulStop() = ref ! GracefulStop

}

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
52
Свой Sink
val s = Sink.actorSubscriber[Int](Props(new MySinkActor))



class MySinkActor extends ActorSubscriber {

override def receive: Receive = {

case ActorSubscriberMessage.OnNext =>

cancel()

request(100)

case ActorSubscriberMessage.OnComplete =>

case ActorSubscriberMessage.OnError =>

}
Итоги
• Стандартные комбинаторы
• Материализация
• Управление жизненным циклом
• Свои stages
54
Итоги
Серебряная пуля
• Более простая технология не подходит
• Backpressure
• Упорядоченные потоки
• Локальность
• Фиксированная топология
56
Когда стоит использовать
• Документация Akka Streams
• Альпака
• reactive-zmq
• reactive-kafka
• Первое видео про Akka Streams
57
Ссылки
Вопросы?
alexey.romanchuk@gmail.com @1esha
Backpressure
60
Событий слишком много
60
Событий слишком много
Отправитель
60
Событий слишком много
Отправитель Получатель
60
Событий слишком много
Отправитель Получатель
61
Событий слишком много
Отправитель Получатель
62
Событий слишком много
Отправитель Получатель
63
Событий слишком много
Отправитель Получатель
64
Событий слишком много
Отправитель Получатель
65
Событий слишком много
Отправитель Получатель
66
Событий слишком много
Отправитель Получатель
67
Событий слишком много
Отправитель Получатель
68
Блокирующий вызов
Отправитель Получатель
68
Блокирующий вызов
Отправитель Получатель
68
Блокирующий вызов
Отправитель Получатель
69
Pull
Отправитель Получатель
69
Pull
Отправитель Получатель
69
Pull
Отправитель Получатель
70
Negative Acknowledge
Отправитель Получатель
70
Negative Acknowledge
Отправитель Получатель
70
Negative Acknowledge
Отправитель Получатель
71
Negative Acknowledge
Отправитель Получатель
72
Dynamic pull-push
Отправитель Получатель
72
Dynamic pull-push
Отправитель Получатель
4
73
Dynamic pull-push
4
Отправитель Получатель
74
Dynamic pull-push
3
Отправитель Получатель
75
Dynamic pull-push
3
Отправитель Получатель
76
Dynamic pull-push
0
Отправитель Получатель
77
Dynamic pull-push
0
Отправитель Получатель
78
Dynamic pull-push
0
Отправитель Получатель
78
Dynamic pull-push
0
Отправитель Получатель
2
79
Dynamic pull-push
2
Отправитель Получатель
80
Dynamic pull-push
0
Отправитель Получатель
81
Balance
Balance
81
Balance
Balance
81
Balance
Balance
82
Broadcast
Broadcast
82
Broadcast
Broadcast
82
Broadcast
Broadcast
83
Zip
Zip
83
Zip
Zip
83
Zip
Zip
84
Unzip
Unzip
84
Unzip
Unzip
84
Unzip
Unzip
85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)

85
Красивый код на стримах
val stream: RunnableGraph[Future[List[TransactionId]]] =

Source

.fromIterator { () => hdfs.listStatusIterator(path) }

.filter(_.isFile)

.flatMapConcat { file =>

val s = fromInputStream(hdfs.open(file.getPath))

Source.fromIterator(() => s.getLines())

}

.throttle(10, 1 second, 0, ThrottleMode.Shaping)

.mapAsyncUnordered(50)(checkFraud)

.collect { case Some(value) => value }

.take(100)

.fold(List.empty[TransactionId])(_ :+ _)

.toMat(Sink.head)(Keep.right)


Практическое применения Akka Streams