Weitere ähnliche Inhalte Ähnlich wie Akka stream (20) Mehr von Masaki Toyoshima (7) Akka stream13. copyright Fringe81 Co.,Ltd.
val source = Source(1 to 10)
val filter = Flow[Int].filter(_ % 2 == 0)
val map = Flow[Int].map(_ * 2)
val sink = Sink.foreach[Int](println)
val runnableGraph =
source.via(filter).via(map).to(sink)
runnableGraph.run()
RunnableGraphの構築と実行
14. copyright Fringe81 Co.,Ltd.
Source(1 to 10)
.filter(_ % 2 == 0)
.map(_ * 2)
.runForeach(println)
RunnableGraphの構築と実行
こう書くことも出来る
val source = Source(1 to 10)
val filter = Flow[Int].filter(_ % 2 == 0)
val map = Flow[Int].map(_ * 2)
val sink = Sink.foreach[Int](println)
val runnableGraph =source.via(filter).via(map).to(sink)
runnableGraph.run()
16. copyright Fringe81 Co.,Ltd.
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
val source = Source(1 to 10)
val filter = Flow[Int].filter(_ % 2 == 0)
val map = Flow[Int].map(_ * 2)
val sink = Sink.foreach[Int](println)
val runnableGraph =
source.via(filter).via(map).to(sink)
runnableGraph.run()
materializer
WHAT
HOW
18. copyright Fringe81 Co.,Ltd.
val future: Future[List[Int]] = ...
val src: Source[List[Int], Unit] = Source(future)
src.mapConcat(identity).map(_ * 2)
def mapConcat[T](f: Out => Iterable[T])
Source[List[Int]]]だとList[Int]が1つ、
ストリームを流れる事になる。
mapConcatを使う事でList要素のIntそれぞれが
ストリームを流れるように出来る。
19. copyright Fringe81 Co.,Ltd.
zipWithIndexを使おうと思った。
が、用意されてなかった。
...作る!
case class ZipWithIndex[T]() extends PushStage[T, (T, Int)] {
var i = -1
override def onPush(elem: T, ctx: Context[(T, Int)]): SyncDirective = {
i += 1
ctx.push((elem, i))
}
}
Source(List("A", "B", "C"))
.transform(() => ZipWithIndex())
.runForeach(println) // (A,0) (B,1) (C, 2)
22. copyright Fringe81 Co.,Ltd.
ちょっとハマった
Iterable とは collection.Immutable.Iterable
// Compile Error
val src = Source(Seq(1,2,3))
// Compile Success
val src = Source(List(1, 2, 3))
Seq は collection.Seq、つまりcollection.Iterable
type Seq[+A] = scala.collection.Seq[A]
val Seq = scala.collection.Seq scala/package.scala
24. copyright Fringe81 Co.,Ltd.
val src: Source[String, Cancellable] =
Source(initialDelay=0.second, interval=100.millis, tick="msg")
val sink: Sink[String, Future[Int]] =
Sink.fold[Int, String](0){ case (sum, _) => sum + 1 }
src sink
100ms毎に"msg"を送出 msgを受信する度に件数カウント
※foldは上流のデータが完了して集計終了となる
Cancellable Future[Int]
ストリームの実行者に渡される値
Materialized Value
25. copyright Fringe81 Co.,Ltd.
val rg1: RunnableGraph[Cancellable] =
src.to(sink)
val rg2: RunnableGraph[Future[Int]] =
src.toMat(sink)(Keep.right)
val rg3: RunnableGraph[(Cancellable, Future[Int])] =
src.toMat(sink)(Keep.both)
val (cancellable, futureInt) = rg3.run()
※src.toMat(sink)(Keep.left)と同義
26. copyright Fringe81 Co.,Ltd.
val src: Source[String, Cancellable] =
Source(initialDelay = 0.second, interval = 100.millis, tick = "msg")
val sink: Sink[String, Future[Int]] =
Sink.fold[Int, String](0){ case (sum, _) => sum + 1 }
val rg: RunnableGraph[(Cancellable, Future[Int])] =
src.toMat(sink)(Keep.both)
val (cancellable, futureInt) = rg.run()
futureInt.foreach(println)
Thread.sleep(1000 * 5)
cancellable.cancel()
29. copyright Fringe81 Co.,Ltd.
(1 to 3)
.map{ i => println(s"A: $i"); i }
.map{ i => println(s"B: $i"); i }
.foreach(i => println(s"C $i"))
A: 1
A: 2
A: 3
B: 1
B: 2
B: 3
C: 1
C: 2
C: 3
Scala Collection
30. copyright Fringe81 Co.,Ltd.
Source(1 to 3)
.map{ i => println(s"A: $i"); i }
.map{ i => println(s"B: $i"); i }
.runForeach(i => println(s"C: $i"))
A: 1
A: 2
B: 1
A: 3
B: 2
C: 1
B: 3
C: 2
C: 3
Akka Stream
37. copyright Fringe81 Co.,Ltd.
// スレッドプールの定義
implicit val system = ActorSystem()
// バッファの定義
implicit val materializer = ActorMaterializer()
akka.actor.default-dispatcher.fork-join-executor.parallelism-max = 1
akka.stream.materializer {
initial-input-buffer-size = 1
max-input-buffer-size = 1
}
42. copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
1
1
2
3
4
5
6
7
...
2
3
1
sink
1
スレッド = 2
バッファ = 2
2
4
1
2
4
5
6
3
スレッドは1本余っているが
バッファ = 2に達しており
バックプレッシャーが効いて
上流は処理が行われない
43. copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
1
1
2
3
4
5
6
7
...
2
3
1
sink
1
スレッド = 2
バッファ = 2
2
4
1
2
4
5
6
3
mapCでは1の処理が終わって
2の処理が始まった。
これにより上流のバッファに1つ空きが
出来たので上流では処理が1つ進む
44. copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
1
1
2
3
4
5
6
7
...
2
3
1
sink
1
スレッド = 2
バッファ = 2
2
4
1
2
4
5
6
3
スレッドは1本余っているが
バッファ = 2に達しており
バックプレッシャーが効いて
上流は処理が行われない
mapCへの
大量流入を
防ぐ
50. copyright Fringe81 Co.,Ltd.
def conflate[S](seed: Out => S)(aggregate: (S, Out) => S)
...
.conflate(List(_)){ (elems, elem) => elem :: elems }
...
← 要素を捨てて良いなら
...
.conflate(identity){ (e, _) => e }
...
BPが効いている間、aggregate関数が実行される
※下流へはList[T]
※下流へはT
まとめあげ効果で下流へのデータ数が減る
下流が要素数に応じて遅くなるなら効果はない
56. copyright Fringe81 Co.,Ltd.
val mapC = Flow() { implicit builder =>
import FlowGraph.Implicits._
val balance = builder.add(Balance[Int](2))
val merge = builder.add(Merge[Int](2))
val map = Flow[Int].map(重い処理)
balance ~> map ~> merge
balance ~> map ~> merge
(balance.in, merge.out)
}
Flowは入力と出力の
ポートを1つずつ持つ
要素追加
データフロー定義
57. copyright Fringe81 Co.,Ltd.
val runnableGraph =
FlowGraph.closed() { implicit builder =>
import FlowGraph.Implicits._
val balance = builder.add(Balance[Int](2))
val merge = builder.add(Merge[Int](2))
val src = Source(1 to 10)
val mapFlow = Flow[Int].map(_ * 2)
val sink = Sink.foreach[Int](println)
src ~> balance ~> map ~> merge ~> sink
balance ~> map ~> merge
}
RunnableGraphを作ることも出来る
58. copyright Fringe81 Co.,Ltd.
val runnableGraph =
FlowGraph.closed() { implicit builder =>
import FlowGraph.Implicits._
val balance = builder.add(Balance[Int](2))
val merge = builder.add(Merge[Int](2))
val src = Source(1 to 10)
val mapFlow = Flow[Int].map(_ * 2)
val sink = Sink.foreach[Int](println)
src ~> balance ~> map ~> merge ~> sink
balance ~> map ~> merge
}
RunnableGraphを作ることも出来る
実際はコードフォーマッタに潰されるので
こう書いています
src ~> balance
balance ~> map ~> merge
balance ~> map ~> merge
merge ~> sink
59. copyright Fringe81 Co.,Ltd.
FlowGraphとMat値
val sink: Sink[Int, Future[Int]] = Sink.fold(0){_ + _}
val rg: RunnableGraph[Future[List[Int]]] =
FlowGraph.closed(sink, sink) ((f1,f2) => Future.sequence(f1 :: f2 :: Nil)) {
implicit builder => (sink1, sink2) =>
import FlowGraph.Implicits._
val balance = builder.add(Balance[Int](2))
Source(1 to 10) ~> balance ~> sink1
balance ~> sink2
}
val ret: Future[List[Int]] = rg.run()
62. copyright Fringe81 Co.,Ltd.
<Fan-In>
Merge 複数入力を1本に
同期することなしに来たものから出力する
Zip 2つの入力AとBを(A,B)にして出力する
同期する
ZipWith 2つの入力AとBを(A,B)にして出力する
同期する
(A,B)を任意の型に加工して出力する
Concat 1つ目の入力を流し終えたら2つ目の入力を流す
FlexiMerge Fan-In型の部品を作るためのベース
67. copyright Fringe81 Co.,Ltd.
val probe = source.runWith(TestSink.probe[Result])
probe
.request(2)
.expectNext(Result(1),Result(2))
.request(100)
.expectNext(Result(3))
.expectComplete()
requestで下流から上流へデータを要求できる
expectNextで流れてくるデータの確認
最後にデータが全て流れ終わったかどうかの確認