akka stream
TRANSCRIPT
![Page 1: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/1.jpg)
copyright Fringe81 Co.,Ltd.
Akka Stream
@mtoyoshi
![Page 2: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/2.jpg)
copyright Fringe81 Co.,Ltd.
AmazonKinesis
1行目
2行目
3行目
4行目
・・・
処理
・・・ 処理
![Page 3: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/3.jpg)
copyright Fringe81 Co.,Ltd.
AmazonKinesis
1行目
2行目
3行目
4行目
・・・
・・・
Akka Actorで処理
![Page 4: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/4.jpg)
copyright Fringe81 Co.,Ltd.
Akka Actor便利ですが
・メッセージ(データ)が型安全でない
・OutOfMemoryに遭遇
・メッセージ送受信の仕組み、汎用的
![Page 5: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/5.jpg)
copyright Fringe81 Co.,Ltd.
Akka Stream?
Typesafeより2015.07に1.0リリース
Akka Actor
Akka Stream
Akka HTTP
![Page 6: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/6.jpg)
copyright Fringe81 Co.,Ltd.
Migration Guide 1.0 to 2.0https://github.com/drewhk/akka/pull/30/files
![Page 7: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/7.jpg)
copyright Fringe81 Co.,Ltd.
RxJava
Reactive Streams(JEP266)
Vert.xAkka
Stream
・・・
Slick3 mongoDB
a standard for asynchronous stream processing
with non-blocking back pressure
その他OSS
![Page 8: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/8.jpg)
copyright Fringe81 Co.,Ltd.
特徴
・バックプレッシャーによりバッファ溢れの危険を回避しつつパフォーマンスにも配慮・ReactiveStreams規格のものと接続可能・ストリームを構成する豊富な部品群・部品群の合成性、拡張性・ビジュアルなグラフDSL
![Page 9: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/9.jpg)
copyright Fringe81 Co.,Ltd.
特徴
・API変更はこれからも続く(1.0->2.0)
・複数ノードにまたがった ストリームの構築は未対応・モナってはいない
![Page 10: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/10.jpg)
copyright Fringe81 Co.,Ltd.
今日はOverview的な話・Akka Streamの構成要素は?
・どういうふうにプログラミングする?
・バックプレッシャーが特徴みたい 概念レベルの理解から一歩進めたい
![Page 11: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/11.jpg)
copyright Fringe81 Co.,Ltd.
部品群を組み合わせて RunnableGraphを作る※1つ以上のSourceと1つ以上のSinkが必要
![Page 12: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/12.jpg)
copyright Fringe81 Co.,Ltd.
部品群を組み合わせて RunnableGraphを作る※1つ以上のSourceと1つ以上のSinkが必要
![Page 13: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/13.jpg)
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の構築と実行
![Page 14: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/14.jpg)
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()
![Page 15: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/15.jpg)
copyright Fringe81 Co.,Ltd.
Source[Int] - Flow[Int,String] - Sink[String]
Source[Int] - Flow[String,Long] Sink[String]
Function1のようにInとOutの型が合えば合成可能
![Page 16: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/16.jpg)
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
![Page 17: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/17.jpg)
copyright Fringe81 Co.,Ltd.
利用可能な処理:
map filter collect take / takeWhile drop / dropWhile flatten fold scan grouped / groupBy recoverなどなど
![Page 18: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/18.jpg)
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それぞれがストリームを流れるように出来る。
![Page 19: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/19.jpg)
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)
![Page 20: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/20.jpg)
copyright Fringe81 Co.,Ltd.
Source#apply
使用頻度が多そう(?)なもの・Iterableから・Iteratorから・Futureから・Fileから↓
SynchronousFileSource(new java.io.File("..."))Source[ByteString]が出来る。
※Akka2.4ベースになればJava7追加のAsynchronousFileChannel等のNIO API使いたいとのこと。
![Page 21: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/21.jpg)
copyright Fringe81 Co.,Ltd.
ちょっとハマった
IterableからSourceを作ることが出来る
// Compile Errorval src = Source(Seq(1,2,3))
// Compile Successval src = Source(List(1, 2, 3))
えっ?
![Page 22: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/22.jpg)
copyright Fringe81 Co.,Ltd.
ちょっとハマった
Iterable とは collection.Immutable.Iterable// Compile Errorval src = Source(Seq(1,2,3))
// Compile Successval src = Source(List(1, 2, 3))
Seq は collection.Seq、つまりcollection.Iterabletype Seq[+A] = scala.collection.Seq[A]val Seq = scala.collection.Seq scala/package.scala
![Page 23: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/23.jpg)
copyright Fringe81 Co.,Ltd.
Source#apply
Source(initialDelay=1.second, interval=100.millis, tick="msg")
100ms毎にmsgというStringを下流に永遠に流す
Tcp().bind("127.0.0.1", 8888)
こういうSourceも作れる
TCP connectionを待ちByteStringをストリームとして処理する
Source(Props[MyActor])
Actorはメッセージ受けて下流になんらかのデータを流していく
![Page 24: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/24.jpg)
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
![Page 25: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/25.jpg)
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)と同義
![Page 26: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/26.jpg)
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()
![Page 27: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/27.jpg)
copyright Fringe81 Co.,Ltd.
・Publisher(Reactive Stream)から
Source#apply
![Page 28: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/28.jpg)
copyright Fringe81 Co.,Ltd.
実行時の挙動確認
通常のScala Collectionとの違い
![Page 29: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/29.jpg)
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: 1A: 2A: 3B: 1B: 2B: 3C: 1C: 2C: 3
Scala Collection
![Page 30: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/30.jpg)
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: 1A: 2B: 1A: 3B: 2C: 1B: 3C: 2C: 3
Akka Stream
![Page 31: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/31.jpg)
copyright Fringe81 Co.,Ltd.
source map:A map:B sink:C
1
123
2
3
1
2
3
1
2
3
各ステージの処理は並行に実行される
![Page 32: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/32.jpg)
copyright Fringe81 Co.,Ltd.
source map:A map:B sink:C
1
123
2
3
1
2
3
1
2
3
ステージ内では一件ずつ逐次処理
![Page 33: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/33.jpg)
copyright Fringe81 Co.,Ltd.
Backpressureの挙動確認スレッドとバッファの関係
![Page 34: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/34.jpg)
copyright Fringe81 Co.,Ltd.
Backpressure?(背圧制御)
上流と下流のデータ流量制御の仕組みバッファ溢れを防ぐ
![Page 35: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/35.jpg)
copyright Fringe81 Co.,Ltd.
PushModel 上流が下流にデータを流し続ける 下流側で処理追いつかずバッファ溢れの可能性
Pull Model 下流から上流にリクエストするとデータが流れる 溢れないが下流側の待ちが大きくなる
dynamic Push/Pull Model 下流から上流にn件リクエストする 上流は下流に要求分流す initial-buffer-size(4), max-buffer-size(16)
![Page 36: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/36.jpg)
copyright Fringe81 Co.,Ltd.
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
![Page 37: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/37.jpg)
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}
![Page 38: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/38.jpg)
copyright Fringe81 Co.,Ltd.
このうちmapCはかなり重い処理とする
source mapA mapB sinkmapC
heavy!
![Page 39: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/39.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
1
2
sink
1
2
2
スレッド = 1バッファ = 1
3
3
4
3
※ は各ステージ上での処理実行を表す
![Page 40: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/40.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
sink
1
2
2
スレッド = 1バッファ = 2
3
2
4
1
2
4
5
6
![Page 41: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/41.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
sink
1
スレッド = 2バッファ = 2
2
4
1
2
4
5
6
3
スレッドは2本あるので1の処理中も上流は処理が行われる
![Page 42: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/42.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
sink
1
スレッド = 2バッファ = 2
2
4
1
2
4
5
6
3
スレッドは1本余っているがバッファ = 2に達しており
バックプレッシャーが効いて上流は処理が行われない
![Page 43: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/43.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
sink
1
スレッド = 2バッファ = 2
2
4
1
2
4
5
6
3
mapCでは1の処理が終わって2の処理が始まった。
これにより上流のバッファに1つ空きが出来たので上流では処理が1つ進む
![Page 44: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/44.jpg)
copyright Fringe81 Co.,Ltd.
source mapA mapB mapC
11234567...
2
3
1
sink
1
スレッド = 2バッファ = 2
2
4
1
2
4
5
6
3
スレッドは1本余っているがバッファ = 2に達しており
バックプレッシャーが効いて上流は処理が行われない
mapCへの大量流入を防ぐ
![Page 45: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/45.jpg)
copyright Fringe81 Co.,Ltd.
バックプレッシャーが効いて上流がストップしている状態を回避/改善しようとすると?
案1:上流の処理を進める為の施策案2:重いmapCを改善する施策
![Page 46: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/46.jpg)
copyright Fringe81 Co.,Ltd.
案1-1:bufferステージを置く
上流の処理を進める施策
![Page 47: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/47.jpg)
copyright Fringe81 Co.,Ltd.
重い処理の前にbufferステージを設けることで上流の処理を進めることが出来る。
ストリーム全体では各ステージのバッファは2としていても
bufferステージのバッファは4といったように異なる値を設定することが出来る。
※なおbufferステージ以外でも個別にバッファ数を指定可能
val buffer = Flow[Int].buffer(4, OverflowStrategy.backpressure)
... mapB.via(buffer).via(mapC) ...
![Page 48: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/48.jpg)
copyright Fringe81 Co.,Ltd.
Flow[Int].buffer(4, OverflowStrategy.dropNew)
ただし設定したバッファ値に達した場合はBPが効く
bufferの前後で極端な処理速度の差がある場合はあまり効果ない
捨てる指示をすれば上流の処理は続行
![Page 49: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/49.jpg)
copyright Fringe81 Co.,Ltd.
案1-2:conflateステージを置く
上流の処理を進める施策
![Page 50: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/50.jpg)
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
まとめあげ効果で下流へのデータ数が減る下流が要素数に応じて遅くなるなら効果はない
![Page 51: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/51.jpg)
copyright Fringe81 Co.,Ltd.
案2-1:mapAsyncステージに変える
重いmapCを改善する施策
![Page 52: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/52.jpg)
copyright Fringe81 Co.,Ltd.
val mapC = Flow[Int].mapAsync(4) { n => Future { 重い処理 } }
処理の終了を待たずに次の処理を開始する
※入力と出力の順序は保証される
mapC
12
34
![Page 53: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/53.jpg)
copyright Fringe81 Co.,Ltd.
案2-2:Fan-Outな部品を用い
parallelに処理する
重いmapCを改善する施策
![Page 54: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/54.jpg)
copyright Fringe81 Co.,Ltd.
Balanceは入力1、出力NなFan-Outな部品均等に下流に流す
Mergeは入力N、出力1なFan-Inな部品同期はしない来たものから下流に流す
※順序は保証されなくなる
balance merge元のmapC元のmapC
![Page 55: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/55.jpg)
copyright Fringe81 Co.,Ltd.
新しいmapC
新しいFlowとしてmapCを定義出来る
balance merge元のmapC元のmapC
balance merge元のmapC元のmapC
新しいmapC
![Page 56: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/56.jpg)
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つずつ持つ
要素追加
データフロー定義
![Page 57: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/57.jpg)
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を作ることも出来る
![Page 58: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/58.jpg)
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 ~> balancebalance ~> map ~> mergebalance ~> map ~> mergemerge ~> sink
![Page 59: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/59.jpg)
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()
![Page 60: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/60.jpg)
copyright Fringe81 Co.,Ltd.
その他のFan-Out, Fan-Inな部品
![Page 61: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/61.jpg)
copyright Fringe81 Co.,Ltd.
<Fan-Out>
Balance 入力を均等に出力に振り分ける
Broadcast 入力を全出力に等しく流す
Unzip (A,B)の入力をAの出力とBの出力に流す
UnZipWith 任意の型の入力をタプルにして出力
FlexiRoute Fan-Out型の部品を作るためのベース
![Page 62: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/62.jpg)
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型の部品を作るためのベース
![Page 63: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/63.jpg)
copyright Fringe81 Co.,Ltd.
Error Handling
![Page 64: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/64.jpg)
copyright Fringe81 Co.,Ltd.
・null要素は流せない・例外が起きるとストリームは失敗として終了
Stop ストリーム失敗終了(default)
Resume 該当の要素を捨てて次の処理を再開
Restart ・該当の要素を捨てる・そのステージを再作成する・処理を再開 ※fold等状態を持つものは状態がクリアされてしまうので注意
Supervision Strategies
![Page 65: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/65.jpg)
copyright Fringe81 Co.,Ltd.
Test
libraryDependencies += Seq( …, "com.typesafe.akka" % "akka-testkit_2.11" % "2.3.14" % "test", "com.typesafe.akka" % "akka-stream-testkit-experimental_2.11" % "1.0" % "test")
![Page 66: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/66.jpg)
copyright Fringe81 Co.,Ltd.
本番用Source
本番用Sink
本番用Flow
本番用Flow
テスト用Source
テスト用Sink
SourceやSinkは外部環境との接続点になりがちでテストしづらい事が多い。テスト時はテスト用のデータを流すSourceとつなげたり、akka-stream-testkitに用意されているTestSinkとつなげて期待通りの結果が流れてくるかを確認したりする。
![Page 67: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/67.jpg)
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で流れてくるデータの確認最後にデータが全て流れ終わったかどうかの確認
![Page 68: Akka stream](https://reader031.vdocuments.mx/reader031/viewer/2022012312/5875d1c21a28ab8f438b55f3/html5/thumbnails/68.jpg)
copyright Fringe81 Co.,Ltd.
ありがとうございました