Slide 1

Slide 1 text

ストリーム処理ことはじめ Akka Streams (と RxJava) 2017/6/24 KANJAVA PARTY 2017 !!! 1 にしかわささき

Slide 2

Slide 2 text

自己紹介 にしかわささき @nishikawasasaki • 広島 → 神戸のシステム屋さん • Java Scala JavaScript… • インフラ AWS Docker KVS… • 検索 データ分析 GIS… • 管理 2017/6/24 KANJAVA PARTY 2017 !!! 2

Slide 3

Slide 3 text

目次 1. Java8 Stream API のおさらい 2. Stream API のその先に 3. Akka-Streams を使う 4. Akka-Streams の便利機能 5. 使用例 6. まとめ 2017/6/24 KANJAVA PARTY 2017 !!! 3

Slide 4

Slide 4 text

「ことはじめ」 ことはじめ【事始め】 の意味 物事に初めて手をつけること。 手始め。着手。「蘭学事始め」 (出典:デジタル大辞泉) 2017/6/24 KANJAVA PARTY 2017 !!! 4

Slide 5

Slide 5 text

アンケート • Java8 を使っていますか? • Java8 からの Stream API は使っていますか? • Scala を使ったことがありますか? • Akka-Streams or RxJava を 使ったことがありますか? 2017/6/24 KANJAVA PARTY 2017 !!! 5

Slide 6

Slide 6 text

Java8 Stream API のおさらい まずはじめに 2017/6/24 KANJAVA PARTY 2017 !!! 6

Slide 7

Slide 7 text

例 1~5 の数値を持つリストがある。 リストの各要素に 3 をかけて リストの各要素から 1 を引いて 2 で割り切れるものだけを表示する。 → 2・8・14 2017/6/24 KANJAVA PARTY 2017 !!! 7

Slide 8

Slide 8 text

回答 1: Java の for 2017/6/24 KANJAVA PARTY 2017 !!! 8 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14

Slide 9

Slide 9 text

回答 2: Java の Stream API 2017/6/24 KANJAVA PARTY 2017 !!! 9 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14

Slide 10

Slide 10 text

回答 3: Scala でメソッドチェーン 2017/6/24 KANJAVA PARTY 2017 !!! 10 // l = [1, 2, 3, 4, 5] l.map(_ * 3) .map(_ - 1) .filter(_ % 2 == 0) .foreach(println) // → 2 8 14

Slide 11

Slide 11 text

回答 1 と 2 の違い 結果は同じ・過程が違う 2017/6/24 KANJAVA PARTY 2017 !!! 11 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14

Slide 12

Slide 12 text

回答 1 と 2 の違い (1の中身) データの変更と絞り込み、処理が混ざっている 2017/6/24 KANJAVA PARTY 2017 !!! 12 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 データの変更 データの絞り込み データの処理 1つの for の中で すべて実行

Slide 13

Slide 13 text

回答 1 と 2 の違い (1の流れ) データの変更と絞り込み、処理が 1 ステップ 2017/6/24 KANJAVA PARTY 2017 !!! 13 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 データ 表示する  3かける  1ひく  2で割り切れないものを捨てる

Slide 14

Slide 14 text

回答 1 と 2 の違い (2の中身) データの変更と絞り込み、処理が分けられている 2017/6/24 KANJAVA PARTY 2017 !!! 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 データの変更 データの絞り込み データの処理 別のブロックで それぞれ実行

Slide 15

Slide 15 text

// l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 回答 1 と 2 の違い (2の流れ) データの変更と絞り込み、処理がいくつかのステップ 2017/6/24 KANJAVA PARTY 2017 !!! 15 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 データ データ データ データ  3かける  1ひく  2で割り切れないものを捨てる 表示する

Slide 16

Slide 16 text

回答 1 と 2 の選択 どちらを お好み えらぶ? 2017/6/24 KANJAVA PARTY 2017 !!! 16 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる 回 答 2 回 答 1

Slide 17

Slide 17 text

回答 1 と 2 の選択 2017/6/24 KANJAVA PARTY 2017 !!! 17 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる !!!突然の仕様変更と追加!!! • 1 ではなく 2 を引く • 2 で割り切れないものを捨てたあと、5 をかける

Slide 18

Slide 18 text

回答 1 と 2 の選択 回答 1 の場合 2017/6/24 KANJAVA PARTY 2017 !!! 18 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる  for の中で どこが仕様変更? どこに仕様追加する?  どんどん for が長くなる

Slide 19

Slide 19 text

回答 1 と 2 の選択 回答 1 の場合 2017/6/24 KANJAVA PARTY 2017 !!! 19 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  2ひく  2で割り切れないものを捨てる  5かける  for の中で どこが仕様変更? どこに仕様追加する? → 仕様調査が大変。  どんどん for が長くなる → 次回の仕様変更は……

Slide 20

Slide 20 text

回答 1 と 2 の選択 回答 2 の場合 2017/6/24 KANJAVA PARTY 2017 !!! 20 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる  データの変更・絞り込みを どこでしているかわかりやすい  チェーンは長くなるけど 処理の流れはつかみやすい 表示する

Slide 21

Slide 21 text

回答 1 と 2 の選択 回答 2 の場合 2017/6/24 KANJAVA PARTY 2017 !!! 21 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ  3かける  2ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる  データの変更・絞り込みを どこでしているかわかりやすい → 仕様調査が楽!  チェーンは長くなるけど 処理の流れはつかみやすい → 次回の仕様変更も楽! データ 表示する  5かける

Slide 22

Slide 22 text

回答 3: Scala でメソッドチェーン (再掲) 2017/6/24 KANJAVA PARTY 2017 !!! 22 // l = [1, 2, 3, 4, 5] l.map(_ * 3) .map(_ - 1) .filter(_ % 2 == 0) .foreach(println) // → 2 8 14 無名関数を 使っている部分を……

Slide 23

Slide 23 text

回答 3: Scala でメソッドチェーン 2017/6/24 KANJAVA PARTY 2017 !!! 23 val multiply3 = (x: Int) => x * 3 val subtraction1 = (x: Int) => x - 1 val mod2Equal0 = (x: Int) => x % 2 == 0 // l = [1, 2, 3, 4, 5] l.map(multiply3) .map(subtraction1) .filter(mod2Equal0) .foreach(println) // → 2 8 14 変数に束縛して 利用するように変更。

Slide 24

Slide 24 text

回答 3: Scala でメソッドチェーン 2017/6/24 KANJAVA PARTY 2017 !!! 24 val multiply3 = (x: Int) => x * 3 val subtraction1 = (x: Int) => x - 1 val mod2Equal0 = (x: Int) => x % 2 == 0 // l = [1, 2, 3, 4, 5] l.map(multiply3) .map(subtraction1) .filter(mod2Equal0) .foreach(println) // → 2 8 14 処理 流れ 処理と流れを 分割することで、 見通しが良くなる。

Slide 25

Slide 25 text

Stream API のメリット 2017/6/24 KANJAVA PARTY 2017 !!! 25 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる Stream API を利用することで  処理と流れを分けて管理できる  自然と細かい単位で分けて処理をおこなうように  結果、仕様変更や追加も楽なコードに

Slide 26

Slide 26 text

Stream API のその先に 2017/6/24 KANJAVA PARTY 2017 !!! 26 // l = [1, 2, 3, 4, 5] l.stream().map(x -> x * 3) .map(x -> x - 1) .filter(x -> x % 2 == 0) .forEach(System.out::println); // → 2 8 14 // l = [1, 2, 3, 4, 5] for (int i : l) { i = i * 3; i = i - 1; if (i % 2 == 0) { System.out.println(i); } } // → 2 8 14 データ データ データ データ 表示する  3かける  1ひく  2で割り切れないものを捨てる データ 表示する  3かける  1ひく  2で割り切れないものを捨てる  もっと速く処理を終わらせるには?  メモリに乗り切らないほど大きなデータを扱うには?  よりわかりやすいプログラムにするには?

Slide 27

Slide 27 text

Stream API のその先に 発展 2017/6/24 KANJAVA PARTY 2017 !!! 27

Slide 28

Slide 28 text

再確認 この時、 どのように処理が進んでいる? 2017/6/24 KANJAVA PARTY 2017 !!! 28 データ データ データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 29

Slide 29 text

再確認 もとのリストの要素を それぞれ 3 倍した結果の リストを作る 2017/6/24 KANJAVA PARTY 2017 !!! 29 [1,2,3,4,5] [] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 30

Slide 30 text

再確認 もとのリストの要素を 1 つ取り出して 3 倍する 2017/6/24 KANJAVA PARTY 2017 !!! 30 [2,3,4,5] [3] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 31

Slide 31 text

再確認 もとのリストの要素を 1 つ取り出して 3 倍する これを繰り返す。 2017/6/24 KANJAVA PARTY 2017 !!! 31 [3,4,5] [3,6] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 32

Slide 32 text

再確認 もとのリストの要素を 1 つ取り出して 3 倍する これを繰り返す。 2017/6/24 KANJAVA PARTY 2017 !!! 32 [4,5] [3,6,9] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 33

Slide 33 text

再確認 もとのリストの要素を 1 つ取り出して 3 倍する これを繰り返す。 2017/6/24 KANJAVA PARTY 2017 !!! 33 [5] [3,6,9,12] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 34

Slide 34 text

再確認 もとのリストの要素を 1 つ取り出して 3 倍する これを繰り返す。 2017/6/24 KANJAVA PARTY 2017 !!! 34 [] [3,6,9,12,15] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 35

Slide 35 text

再確認 リストの要素を それぞれ 3 倍した結果の リストが (新しく) 作られる 2017/6/24 KANJAVA PARTY 2017 !!! 35 [1,2,3,4,5] [3,6,9,12,15] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 36

Slide 36 text

再確認 同様に、 リストの要素を それぞれ 1 引いた結果の リストが作られる 2017/6/24 KANJAVA PARTY 2017 !!! 36 [1,2,3,4,5] [3,6,9,12,15] [2,5,8,11,14] データ  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 37

Slide 37 text

再確認 同様に、 リストの要素を それぞれ 2 で割り切れるか確認し 割り切れないものを除いた リストが作られる 2017/6/24 KANJAVA PARTY 2017 !!! 37 [1,2,3,4,5] [3,6,9,12,15] [2,5,8,11,14] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 38

Slide 38 text

再確認 1 ブロックずつ処理が進んでいく 2017/6/24 KANJAVA PARTY 2017 !!! 38 [1,2,3,4,5] [3,6,9,12,15] [2,5,8,11,14] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 39

Slide 39 text

再確認 1 ブロックずつ処理が進んでいく ↓ ブロックのデータがそろうまで 待ち合わせが発生している 2017/6/24 KANJAVA PARTY 2017 !!! 39 [1,2,3,4,5] [3,6,9,12,15] [2,5,8,11,14] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 40

Slide 40 text

再確認 1 ブロックずつ処理が進んでいく ↓ ブロックのデータがそろうまで 待ち合わせが発生している ↓ ・処理がとても重たい時は? ・ブロックに乗り切らない時は? 2017/6/24 KANJAVA PARTY 2017 !!! 40 [1,2,3,4,5] [3,6,9,12,15] [2,5,8,11,14] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 41

Slide 41 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 2017/6/24 KANJAVA PARTY 2017 !!! 41 [1,2,3,4,5] [] [] []  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 42

Slide 42 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 2017/6/24 KANJAVA PARTY 2017 !!! 42 [2,3,4,5] [3] [] []  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 43

Slide 43 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 処理が済んだデータは 次のブロックに進んでいく。 2017/6/24 KANJAVA PARTY 2017 !!! 43 [4,5] [6,9] [2] []  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 44

Slide 44 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 処理が済んだデータは 次のブロックに進んでいく。 2017/6/24 KANJAVA PARTY 2017 !!! 44 [] [12,15] [5,8] [2]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 45

Slide 45 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 処理が済んだデータは 次のブロックに進んでいく。 2017/6/24 KANJAVA PARTY 2017 !!! 45 [] [] [11,14] [2,8]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 46

Slide 46 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 処理が済んだデータは 次のブロックに進んでいく。 2017/6/24 KANJAVA PARTY 2017 !!! 46 [] [] [] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 47

Slide 47 text

理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 → Akka-Streams で解決 2017/6/24 KANJAVA PARTY 2017 !!! 47 [] [] [] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる

Slide 48

Slide 48 text

Akka-Streams を使う 動きの理解 2017/6/24 KANJAVA PARTY 2017 !!! 48

Slide 49

Slide 49 text

Akka-Streams を使う 先ほどの例に Akka-Streams を使ってみる 2017/6/24 KANJAVA PARTY 2017 !!! 49 val l = List(1, 2, 3, 4, 5) l.map(_ * 3) .map(_ - 1) .filter(_ % 2 == 0) .foreach(println) // → 2 8 14 通常の操作

Slide 50

Slide 50 text

Akka-Streams を使う 先ほどの例に Akka-Streams を使ってみる → ほとんど違和感なく使うことができる 2017/6/24 KANJAVA PARTY 2017 !!! 50 val l = Source(List(1, 2, 3, 4, 5)) l.map(_ * 3) .map(_ - 1) .filter(_ % 2 == 0) .runForeach(println) // → 2 8 14 val l = List(1, 2, 3, 4, 5) l.map(_ * 3) .map(_ - 1) .filter(_ % 2 == 0) .foreach(println) // → 2 8 14 通常の操作 Akka-Streams 使用 (抜粋)

Slide 51

Slide 51 text

Akka-Streams を使わない場合 通常の map 処理にするため少し変更 2017/6/24 KANJAVA PARTY 2017 !!! 51 def processingStageNormal(name: String, s: String): String = { // (省略) 遅い処理の代わり。処理開始前と後にログを出力する } List("Hello", "Streams", "World!") .map(s => processingStageNormal("A", s)) .map(s => processingStageNormal("B", s)) .map(s => processingStageNormal("C", s)) .foreach(s ⇒ println("Got output " + s))

Slide 52

Slide 52 text

Akka-Streams を使わない場合 (結果) 2017/6/24 KANJAVA PARTY 2017 !!! 52 A started processing Hello on thread main A finished processing Hello A started processing Streams on thread main A finished processing Streams A started processing World! on thread main A finished processing World! B started processing Hello on thread main B finished processing Hello B started processing Streams on thread main B finished processing Streams B started processing World! on thread main B finished processing World! C started processing Hello on thread main C finished processing Hello C started processing Streams on thread main C finished processing Streams C started processing World! on thread main C finished processing World! Got output Hello Got output Streams Got output World!

Slide 53

Slide 53 text

Akka-Streams を使わない場合 (結果) 2017/6/24 KANJAVA PARTY 2017 !!! 53 A started processing Hello on thread main A finished processing Hello A started processing Streams on thread main A finished processing Streams A started processing World! on thread main A finished processing World! B started processing Hello on thread main B finished processing Hello B started processing Streams on thread main B finished processing Streams B started processing World! on thread main B finished processing World! C started processing Hello on thread main C finished processing Hello C started processing Streams on thread main C finished processing Streams C started processing World! on thread main C finished processing World! Got output Hello Got output Streams Got output World! A B C Hello Streams World! List("Hello", "Streams", "World!") .map(s => processingStageNormal("A", s)) .map(s => processingStageNormal("B", s)) .map(s => processingStageNormal("C", s)) .foreach(s ⇒ println("Got output " + s)) 処理がすべて終わってから 次の処理に進んでいる Hello Streams World! Hello

Slide 54

Slide 54 text

Akka-Streams の例 2017/6/24 KANJAVA PARTY 2017 !!! 54 def processingStage(name: String): Flow[String, String, NotUsed] = Flow[String].map { s ⇒ // (省略) 遅い処理の代わり。処理開始前と後にログを出力する } val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) completion.onComplete(_ => system.terminate()) http://akka.io/blog/2016/07/06/threading-and-concurrency-in-akka-streams-explained

Slide 55

Slide 55 text

Akka-Streams の例 (結果) 2017/6/24 KANJAVA PARTY 2017 !!! 55 A started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-5 A finished processing Hello A started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-5 B started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-8 B finished processing Hello A finished processing Streams A started processing World! on thread twitter-convert-akka.actor.default-dispatcher-5 C started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-6 B started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-8 B finished processing Streams A finished processing World! C finished processing Hello C started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-6 Got output Hello B started processing World! on thread twitter-convert-akka.actor.default-dispatcher-8 C finished processing Streams B finished processing World! Got output Streams C started processing World! on thread twitter-convert-akka.actor.default-dispatcher-6 C finished processing World! Got output World!

Slide 56

Slide 56 text

Akka-Streams の例 (結果) 2017/6/24 KANJAVA PARTY 2017 !!! 56 A started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-5 A finished processing Hello A started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-5 B started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-8 B finished processing Hello A finished processing Streams A started processing World! on thread twitter-convert-akka.actor.default-dispatcher-5 C started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-6 B started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-8 B finished processing Streams A finished processing World! C finished processing Hello C started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-6 Got output Hello B started processing World! on thread twitter-convert-akka.actor.default-dispatcher-8 C finished processing Streams B finished processing World! Got output Streams C started processing World! on thread twitter-convert-akka.actor.default-dispatcher-6 C finished processing World! Got output World! A B C Hello Hello Hello Streams World! Streams Streams World! val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) 処理が終わったデータから 次の処理に進んでいる

Slide 57

Slide 57 text

Akka-Streams の例 (async 無し) 2017/6/24 KANJAVA PARTY 2017 !!! 57 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) async しないとどうなるのか?

Slide 58

Slide 58 text

Akka-Streams の例 (async 無し) 2017/6/24 KANJAVA PARTY 2017 !!! 58 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")) .via(processingStage("B")) .via(processingStage("C")) .runWith(Sink.foreach(s ⇒ println("Got output " + s))) async を消して動かしてみる

Slide 59

Slide 59 text

Akka-Streams の例 (async 無しの結果) 2017/6/24 KANJAVA PARTY 2017 !!! 59 A started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing Hello B started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing Hello C started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing Hello Got output Hello A started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing Streams B started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing Streams C started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing Streams Got output Streams A started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing World! B started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing World! C started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing World! Got output World!

Slide 60

Slide 60 text

Akka-Streams の例 (async 無しの結果) 2017/6/24 KANJAVA PARTY 2017 !!! 60 A started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing Hello B started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing Hello C started processing Hello on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing Hello Got output Hello A started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing Streams B started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing Streams C started processing Streams on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing Streams Got output Streams A started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 A finished processing World! B started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 B finished processing World! C started processing World! on thread twitter-convert-akka.actor.default-dispatcher-4 C finished processing World! Got output World! A B C Hello Streams val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")) .via(processingStage("B")) .via(processingStage("C")) .runWith(Sink.foreach(s ⇒ println("Got output " + s))) 処理がすべて終わってから 次の処理に進んでいる Hello Streams Hello Streams

Slide 61

Slide 61 text

Akka-Streams で理想を実現 • Akka-Streams を導入することで 処理の並列処理が可能となる • Akka-Streams の async (とその他) を使うと 処理間の非同期も可能に 2017/6/24 KANJAVA PARTY 2017 !!! 61

Slide 62

Slide 62 text

Akka-Streams の部品 • Source • Flow • Sink 2017/6/24 KANJAVA PARTY 2017 !!! 62 Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async Sink.foreach(s ⇒ println("Got output " + s))

Slide 63

Slide 63 text

Akka-Streams の部品 • Source Output を持つ • Flow Input と Output を持つ • Sink Input を持つ 2017/6/24 KANJAVA PARTY 2017 !!! 63 Source Flow Sink

Slide 64

Slide 64 text

Akka-Streams の部品を組み合わせる Source 1 つと Flow いくつかと Sink 1 つを 組み合わせて「閉じた」処理の流れを形成する → RunnableGraph 2017/6/24 KANJAVA PARTY 2017 !!! 64 Source Sink Source Flow Sink Flow Sink Source Flow Flow は 使わなくても良い 組み合わせ方は自由。 それぞれ 再利用も可能。

Slide 65

Slide 65 text

Akka-Streams の部品を組み合わせる 2017/6/24 KANJAVA PARTY 2017 !!! 65 Source 1 つと Flow 3 つと Sink 1 つを 組み合わせて作られた処理の流れ (RunnableGraph) val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink

Slide 66

Slide 66 text

Akka-Streams の部品を組み合わせて実行 2017/6/24 KANJAVA PARTY 2017 !!! 66 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink Hello Streams World! Source から Flow を通って Sink へデータが流れる

Slide 67

Slide 67 text

Akka-Streams の部品を組み合わせて実行 2017/6/24 KANJAVA PARTY 2017 !!! 67 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink Hello Streams World! Source から Flow を通って Sink へデータが流れる

Slide 68

Slide 68 text

Akka-Streams の部品を組み合わせて実行 2017/6/24 KANJAVA PARTY 2017 !!! 68 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink Hello Streams World! Source から Flow を通って Sink へデータが流れる

Slide 69

Slide 69 text

Akka-Streams の部品を組み合わせて実行 2017/6/24 KANJAVA PARTY 2017 !!! 69 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink Hello Streams World! Source から Flow を通って Sink へデータが流れる

Slide 70

Slide 70 text

Akka-Streams の部品を組み合わせて実行 2017/6/24 KANJAVA PARTY 2017 !!! 70 val completion = Source(List("Hello", "Streams", "World!")) .via(processingStage("A")).async .via(processingStage("B")).async .via(processingStage("C")).async .runWith(Sink.foreach(s ⇒ println("Got output " + s))) Source Flow Flow Flow Sink Hello Streams World! Source から Flow を通って Sink へデータが流れる

Slide 71

Slide 71 text

Akka-Streams の便利機能 (いくつか) 使いこなし 2017/6/24 KANJAVA PARTY 2017 !!! 71

Slide 72

Slide 72 text

便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 72 • Framing 指定のバイト数やデリミタで区切られたデータとして扱う → 使われるメモリ量をコントロール ex. 256 バイトずつ読み込みカンマ区切りに Flow[ByteString] .via(Framing.delimiter(ByteString(","), 256, allowTruncation = true))

Slide 73

Slide 73 text

便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 73 • Throttle 流れの途中で流量制限をおこなう → 負荷のコントロール ex. 1 秒に 1 データだけ流す Source.repeat("test") .throttle(1, 1.second, 0, ThrottleMode.shaping) .runForeach(println)

Slide 74

Slide 74 text

便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 74 • Fan-in/Fan-out 流れを分岐したり合流したりする Source Flow Flow Flow Broadcast Sink Merge Sink

Slide 75

Slide 75 text

便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 75 • GraphDSL 流れを DSL で表現することができる Source Flow Flow Flow Bcast Sink Merge Sink source ~> bcast ~> flow1 ~> sink1 bcast ~> flow2 ~> merge ~> sink2 bcast ~> flow3 ~> merge かなり直感的に 記述可能

Slide 76

Slide 76 text

まとめ さいごに 2017/6/24 KANJAVA PARTY 2017 !!! 76

Slide 77

Slide 77 text

まとめ 1/2 2017/6/24 KANJAVA PARTY 2017 !!! 77 Stream API を利用すると 自然に処理の内容と流れが分かれたコードとなる 処理の内容と流れが分かれたコードは 仕様変更や追加に強い見通しの良い作りになる

Slide 78

Slide 78 text

まとめ 1/2 2017/6/24 KANJAVA PARTY 2017 !!! 78 Stream API を利用すると 自然に処理の内容と流れが分かれたコードとなる 処理の内容と流れが分かれたコードは 仕様変更や追加に強い見通しの良い作りになる →「普段やっていないことは複雑な問題に適用できない」

Slide 79

Slide 79 text

まとめ 2/2 2017/6/24 KANJAVA PARTY 2017 !!! 79 Akka-Streams 利用することで 入力・変更・出力の役割で分割された コードを書くことができる 設計したデータの流れを実装に落とし込みやすくなる 並列処理や非同期処理を助けてくれ、 処理の流れが複雑になっても 用意された多くの機能や表現力が助けてくれる