Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

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

nishikawasasaki

June 24, 2017
Tweet

More Decks by nishikawasasaki

Other Decks in Programming

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide


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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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 の中で
    すべて実行

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

  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
    データの変更
    データの絞り込み
    データの処理
    別のブロックで
    それぞれ実行

    View full-size slide

  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で割り切れないものを捨てる
    表示する

    View full-size slide

  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

    View full-size slide

  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 をかける

    View full-size slide

  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 が長くなる

    View full-size slide

  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 が長くなる
    → 次回の仕様変更は……

    View full-size slide

  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で割り切れないものを捨てる
     データの変更・絞り込みを
    どこでしているかわかりやすい
     チェーンは長くなるけど
    処理の流れはつかみやすい
    表示する

    View full-size slide

  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かける

    View full-size slide

  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
    無名関数を
    使っている部分を……

    View full-size slide

  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
    変数に束縛して
    利用するように変更。

    View full-size slide

  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
    処理
    流れ
    処理と流れを
    分割することで、
    見通しが良くなる。

    View full-size slide

  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 を利用することで
     処理と流れを分けて管理できる
     自然と細かい単位で分けて処理をおこなうように
     結果、仕様変更や追加も楽なコードに

    View full-size slide

  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で割り切れないものを捨てる
     もっと速く処理を終わらせるには?
     メモリに乗り切らないほど大きなデータを扱うには?
     よりわかりやすいプログラムにするには?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

  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で割り切れないものを捨てる

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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
    通常の操作

    View full-size slide

  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 使用 (抜粋)

    View full-size slide

  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))

    View full-size slide

  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!

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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!

    View full-size slide

  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)))
    処理が終わったデータから
    次の処理に進んでいる

    View full-size slide

  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 しないとどうなるのか?

    View full-size slide

  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 を消して動かしてみる

    View full-size slide

  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!

    View full-size slide

  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

    View full-size slide

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

    View full-size slide

  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))

    View full-size slide

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

    View full-size slide

  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 は
    使わなくても良い
    組み合わせ方は自由。
    それぞれ
    再利用も可能。

    View full-size slide

  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

    View full-size slide

  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 へデータが流れる

    View full-size slide

  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 へデータが流れる

    View full-size slide

  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 へデータが流れる

    View full-size slide

  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 へデータが流れる

    View full-size slide

  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 へデータが流れる

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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
    かなり直感的に
    記述可能

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide