ストリーム処理ことはじめ (公開用)

ストリーム処理ことはじめ (公開用)

「KANJAVA PARTY 2017 !!!」で発表した内容から
事例部分など一部除いた内容のスライド資料です。

KANJAVA PARTY 2017 !!!
https://kanjava.connpass.com/event/56152/

E35e0e7d3f6105befd7096b7ee558911?s=128

nishikawasasaki

June 24, 2017
Tweet

Transcript

  1. ストリーム処理ことはじめ Akka Streams (と RxJava) 2017/6/24 KANJAVA PARTY 2017 !!!

    1 にしかわささき
  2. 自己紹介 にしかわささき @nishikawasasaki • 広島 → 神戸のシステム屋さん • Java Scala

    JavaScript… • インフラ AWS Docker KVS… • 検索 データ分析 GIS… • 管理 2017/6/24 KANJAVA PARTY 2017 !!! 2
  3. 目次 1. Java8 Stream API のおさらい 2. Stream API のその先に

    3. Akka-Streams を使う 4. Akka-Streams の便利機能 5. 使用例 6. まとめ 2017/6/24 KANJAVA PARTY 2017 !!! 3
  4. 「ことはじめ」 ことはじめ【事始め】 の意味 物事に初めて手をつけること。 手始め。着手。「蘭学事始め」 (出典:デジタル大辞泉) 2017/6/24 KANJAVA PARTY 2017

    !!! 4
  5. アンケート • Java8 を使っていますか? • Java8 からの Stream API は使っていますか?

    • Scala を使ったことがありますか? • Akka-Streams or RxJava を 使ったことがありますか? 2017/6/24 KANJAVA PARTY 2017 !!! 5
  6. Java8 Stream API のおさらい まずはじめに 2017/6/24 KANJAVA PARTY 2017 !!!

    6
  7. 例 1~5 の数値を持つリストがある。 リストの各要素に 3 をかけて リストの各要素から 1 を引いて 2

    で割り切れるものだけを表示する。 → 2・8・14 2017/6/24 KANJAVA PARTY 2017 !!! 7
  8. 回答 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
  9. 回答 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
  10. 回答 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
  11. 回答 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
  12. 回答 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 の中で すべて実行
  13. 回答 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で割り切れないものを捨てる
  14. 回答 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 データの変更 データの絞り込み データの処理 別のブロックで それぞれ実行
  15. // 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で割り切れないものを捨てる 表示する
  16. 回答 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
  17. 回答 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 をかける
  18. 回答 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 が長くなる
  19. 回答 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 が長くなる → 次回の仕様変更は……
  20. 回答 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で割り切れないものを捨てる  データの変更・絞り込みを どこでしているかわかりやすい  チェーンは長くなるけど 処理の流れはつかみやすい 表示する
  21. 回答 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かける
  22. 回答 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 無名関数を 使っている部分を……
  23. 回答 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 変数に束縛して 利用するように変更。
  24. 回答 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 処理 流れ 処理と流れを 分割することで、 見通しが良くなる。
  25. 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 を利用することで  処理と流れを分けて管理できる  自然と細かい単位で分けて処理をおこなうように  結果、仕様変更や追加も楽なコードに
  26. 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で割り切れないものを捨てる  もっと速く処理を終わらせるには?  メモリに乗り切らないほど大きなデータを扱うには?  よりわかりやすいプログラムにするには?
  27. Stream API のその先に 発展 2017/6/24 KANJAVA PARTY 2017 !!! 27

  28. 再確認 この時、 どのように処理が進んでいる? 2017/6/24 KANJAVA PARTY 2017 !!! 28 データ

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

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

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

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

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

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

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

    PARTY 2017 !!! 35 [1,2,3,4,5] [3,6,9,12,15] データ データ  3かける  1ひく 表示する  2で割り切れないものを捨てる
  36. 再確認 同様に、 リストの要素を それぞれ 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で割り切れないものを捨てる
  37. 再確認 同様に、 リストの要素を それぞれ 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で割り切れないものを捨てる
  38. 再確認 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で割り切れないものを捨てる
  39. 再確認 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で割り切れないものを捨てる
  40. 再確認 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で割り切れないものを捨てる
  41. 理想像 ・ブロック単位ではなく データ単位で進むこと。 ・ブロック内を並列処理したい。 2017/6/24 KANJAVA PARTY 2017 !!! 41

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

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

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

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

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

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

    2017 !!! 47 [] [] [] [2,8,14]  3かける  1ひく 表示する  2で割り切れないものを捨てる
  48. Akka-Streams を使う 動きの理解 2017/6/24 KANJAVA PARTY 2017 !!! 48

  49. 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 通常の操作
  50. 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 使用 (抜粋)
  51. 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))
  52. 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!
  53. 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
  54. 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
  55. 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!
  56. 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))) 処理が終わったデータから 次の処理に進んでいる
  57. 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 しないとどうなるのか?
  58. 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 を消して動かしてみる
  59. 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!
  60. 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
  61. Akka-Streams で理想を実現 • Akka-Streams を導入することで 処理の並列処理が可能となる • Akka-Streams の async

    (とその他) を使うと 処理間の非同期も可能に 2017/6/24 KANJAVA PARTY 2017 !!! 61
  62. 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))
  63. Akka-Streams の部品 • Source Output を持つ • Flow Input と

    Output を持つ • Sink Input を持つ 2017/6/24 KANJAVA PARTY 2017 !!! 63 Source Flow Sink
  64. 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 は 使わなくても良い 組み合わせ方は自由。 それぞれ 再利用も可能。
  65. 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
  66. 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 へデータが流れる
  67. 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 へデータが流れる
  68. 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 へデータが流れる
  69. 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 へデータが流れる
  70. 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 へデータが流れる
  71. Akka-Streams の便利機能 (いくつか) 使いこなし 2017/6/24 KANJAVA PARTY 2017 !!! 71

  72. 便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 72 • Framing 指定のバイト数やデリミタで区切られたデータとして扱う

    → 使われるメモリ量をコントロール ex. 256 バイトずつ読み込みカンマ区切りに Flow[ByteString] .via(Framing.delimiter(ByteString(","), 256, allowTruncation = true))
  73. 便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 73 • Throttle 流れの途中で流量制限をおこなう

    → 負荷のコントロール ex. 1 秒に 1 データだけ流す Source.repeat("test") .throttle(1, 1.second, 0, ThrottleMode.shaping) .runForeach(println)
  74. 便利機能の紹介 2017/6/24 KANJAVA PARTY 2017 !!! 74 • Fan-in/Fan-out 流れを分岐したり合流したりする

    Source Flow Flow Flow Broadcast Sink Merge Sink
  75. 便利機能の紹介 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 かなり直感的に 記述可能
  76. まとめ さいごに 2017/6/24 KANJAVA PARTY 2017 !!! 76

  77. まとめ 1/2 2017/6/24 KANJAVA PARTY 2017 !!! 77 Stream API

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

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

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