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

Akkaちゃんと遊ぼう! Akka Streams で作るリアルタイムサーバ #scalafukuoka / Implement stream server with Akka Streams and WebSocket

Akkaちゃんと遊ぼう! Akka Streams で作るリアルタイムサーバ #scalafukuoka / Implement stream server with Akka Streams and WebSocket

「Akka」と聞くと、Actor、並列性、永続化、クラスタなどその特性や機能について目が行きがちです。本セッションでは、機能そのものの話やメリット・デメリットはいったんおいておき、「Akka を作って例えばどのようなものが作れるか?」にフォーカスします。実際に動くチャットサーバやメディアサーバの実装を見ながら、Akkaのどのような機能を組み合わせて実現しているかお話します。

Yusuke Wada

July 29, 2017
Tweet

More Decks by Yusuke Wada

Other Decks in Technology

Transcript

  1. "LLBͪΌΜͱ༡΅͏ʂ
    "LLB4USFBNTͰ࡞ΔϦΞϧλΠϜαʔό
    TDBMBGVLVPLB

    View Slide

  2. ࣗݾ঺հ
    w ࿨ా༞հ
    w ೥݄೔
    w ۀ຿4DBMB1MBZ
    w 5XJUUFS!XBEEZ@V
    w झຯ૲໺ٿ

    ɹɹɹΠΧͷ΍ͭ ৽ࢀ

    View Slide

  3. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO
    NJO
    NJO
    NJO
    NJO
    NJO
    NJO
    NJO

    View Slide


  4. w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide

  5. "LLBͪΌΜͷಛ௃ฒྻ෼ࢄॲཧϥΠϒϥϦ܈
    "DUPST
    $MVTUFS
    1FSTJTUFODF
    4USFBNT
    )551





    ϚϧνεϨουʹ୅ΘΔ৽͍͠ॲཧͷ࣮ߦ୯Ґ
    ϩʔΧϧʗϦϞʔτ໰Θͣ࢓ࣄΛ౤͛Δͱྑ͍ײ͡ʹॲཧͯ͘͠Ε
    Δʮ"DPUSͷू·ΓʯΛͭ͘Γ͋͛Δ
    "DUPSͷঢ়ଶΛӬଓԽ͢ΔͨΊͷ࢓૊ΈɻॲཧΛ੥͚ෛ͍ͬͯͨ
    "DUPS͕Τϥʔʹͳͬͯ͠·ͬͨ৔߹ɺผͷ"DUPS͕Ҿ͖ܧ͛Δ
    σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    )551ͱ্ड़ͷΑ͏ͳ࢓૊ΈΛόοΫΤϯυͱͯͭ͠ͳ͙

    View Slide

  6. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    "DUPST
    $MVTUFS
    1FSTJTUFODF
    4USFBNT
    )551





    ϚϧνεϨουʹ୅ΘΔ৽͍͠ॲཧͷ࣮ߦ୯Ґ
    ϩʔΧϧʗϦϞʔτ໰Θͣ࢓ࣄΛ౤͛Δͱྑ͍ײ͡ʹॲཧͯ͘͠Ε
    Δʮ"DPUSͷू·ΓʯΛͭ͘Γ͋͛Δ
    "DUPSͷঢ়ଶΛӬଓԽ͢ΔͨΊͷ࢓૊ΈɻॲཧΛ੥͚ෛ͍ͬͯͨ
    "DUPS͕Τϥʔʹͳͬͯ͠·ͬͨ৔߹ɺผͷ"DUPS͕Ҿ͖ܧ͛Δ
    σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    )551ͱ্ड़ͷΑ͏ͳ࢓૊ΈΛόοΫΤϯυͱͯͭ͠ͳ͙

    View Slide

  7. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    4USFBNT σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈

    View Slide

  8. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    4USFBNT σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    manuzhang/awesome-streaming: a curated list of awesome
    streaming frameworks, applications, etc https://github.com/
    manuzhang/awesome-streaming
    وॏͳετϦʔϛϯάϥΠϒϥϦ

    View Slide

  9. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    4USFBNT σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    وॏͳετϦʔϛϯάϥΠϒϥϦ
    ετϦʔϛϯάόοΫΤϯυΛߏ
    ஙͰ͖Δͱ৭ʑͱԠ༻͕ޮ͘ͷͰ
    ͸ʂʁ
    Streaming
    Req/Res
    Service
    Batch

    View Slide

  10. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    4USFBNT σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    وॏͳετϦʔϛϯάϥΠϒϥϦ
    ετϦʔϛϯάόοΫΤϯυΛߏ
    ஙͰ͖Δͱ৭ʑͱԠ༻͕ޮ͘ͷͰ
    ͸ʂʁ
    ྲྀΕདྷͯΔ
    Google Trend
    “Akka Streams”

    View Slide

  11. "LLBͪΌΜͷಛ௃"LLB4USFBNT͕࢖͑Δ
    4USFBNT σʔλͷ։࢝ͱऴ͕ྃఆٛ͞Ε͍ͳ͍λΠϓͷ࢓ࣄʢετϦʔϛϯ
    άαʔϏεʣΛ͜ͳͨ͢Ίͷ෦඼܈
    وॏͳετϦʔϛϯάϥΠϒϥϦ
    ετϦʔϛϯάόοΫΤϯυΛߏ
    ஙͰ͖Δͱ৭ʑͱԠ༻͕ޮ͘ͷͰ
    ͸ʂʁ
    ྲྀΕདྷͯΔ
    Google Trend
    “Akka Streams”
    ͔ͳΓΠέͯΔϥΠϒϥϦͳͷ͸
    ؒҧ͍ͳ͍Μ͚ͩΕͲ΋

    View Slide

  12. "LLBͪΌΜͷಛ௃ͱ͖ͬͭʹ͍͘
    ӳޠ͹͔ͬ΍Μ͚ʂ
    http://doc.akka.io/docs/akka/current/scala/guide/introduction.html http://freecontent.manning.com/akka-in-action-why-use-clustering/

    View Slide


  13. গ͠Ͱ΋਌͠ΈΛ࣋ͬͯཉ͍͠

    View Slide


  14. Akka Streams ͱ͸!!
    Έ͍ͨͳ࿩Λ͢ΔΑΓ΋

    View Slide


  15. श͏ΑΓ׳ΕΖਫ਼ਆ

    View Slide


  16. ʮ͜Μͳ΋ͷ΋࡞ΕͨΑʂʯ

    View Slide


  17. ଱ো֐ੑ ύϑΥʔϚϯε ߴՄ༻ੑ
    Messageۦಈͱ͸
    Έ͍ͨͳ࿩͸Ұ੾͋Γ·ͤΜ

    View Slide


  18. w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide

  19. ετϦʔϜॲཧͷߟ͑ํ
    ҰൠతʹετϦʔϜͱ͍͑͹
    σʔλൃੜݯ 4PVSDF

    σʔλड৴ऀ 4JOL

    σʔλ੍ޚ 'MPX

    ͜ΕΒͷ࢓ࣄΛ΍Δਓ͕͍Ε͹ͳΜͱ͔ͳΔ
    "LLB4USFBNTʹ͓͍ͯ͸Ͳ͏͍͏ѻ͍͔ʁ

    View Slide

  20. ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞ΔήʔϜ
    "LLB4USFBNTͰετϦʔϜॲཧΛఆٛ͢Δ৔߹ɿ
    ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δɻ
    ࡞ͬͨάϥϑΛ࣮ߦ͢Δɻ

    View Slide

  21. ͋ͷ͊͞
    ͓·͑͸ҰମԿΛݴ͍ͬͯΔΜͩ

    View Slide

  22. ίʔυϕʔεͰݟ͍͖ͯ·͢ʂ
    $ sbt new playframework/play-scala-seed.g8
    [info] Loading global plugins from /Users/wada.yusuke/.sbt/0.13/plugins
    [info] Set current project to developers-io-2017 (in build file:/Users/
    wada.yusuke/.ghq/github.com/cm-wada-yusuke/developers-io-2017/)
    This template generates a Play Scala project
    name [play-scala-seed]: akka-streams-starter
    organization [com.example]:
    scalatestplusplay_version [3.1.1]:
    play_version [2.6.2]:
    Template applied in ./akka-streams-starter

    View Slide

  23. ίʔυϕʔεͰݟ͍͖ͯ·͢ʂ
    $ sbt new playframework/play-scala-seed.g8
    [info] Loading global plugins from /Users/wada.yusuke/.sbt/0.13/plugins
    [info] Set current project to developers-io-2017 (in build file:/Users/
    wada.yusuke/.ghq/github.com/cm-wada-yusuke/developers-io-2017/)
    This template generates a Play Scala project
    name [play-scala-seed]: akka-streams-starter
    organization [com.example]:
    scalatestplusplay_version [3.1.1]:
    play_version [2.6.2]:
    Template applied in ./akka-streams-starter
    1MBZʹ͸BLLB͕ಉࠝ͞Ε͍ͯ·͢

    View Slide

  24. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε
    import akka.actor.ActorSystem
    import akka.stream.ActorMaterializer
    import akka.stream.scaladsl.{ Flow, Sink, Source }
    object StreamApplication {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    def main(args: Array[String]): Unit = {
    val source = Source(1 to 100)
    val flow = Flow.fromFunction[Int, Int](_ * 3)
    val sink = Sink.foreach(println)
    val graph = source.via(flow).to(sink)
    graph.run()
    }
    }

    View Slide

  25. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε
    import akka.actor.ActorSystem
    import akka.stream.ActorMaterializer
    import akka.stream.scaladsl.{ Flow, Sink, Source }
    object StreamApplication {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    def main(args: Array[String]): Unit = {
    val source = Source(1 to 100)
    val flow = Flow.fromFunction[Int, Int](_ * 3)
    val sink = Sink.foreach(println)
    val graph = source.via(flow).to(sink)
    graph.run()
    }
    }

    View Slide

  26. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε
    import akka.actor.ActorSystem
    import akka.stream.ActorMaterializer
    import akka.stream.scaladsl.{ Flow, Sink, Source }
    object StreamApplication {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    def main(args: Array[String]): Unit = {
    val source = Source(1 to 100)
    val flow = Flow.fromFunction[Int, Int](_ * 3)
    val sink = Sink.foreach(println)
    val graph = source.via(flow).to(sink)
    graph.run()
    }
    }
    TCUlSVO.BJO4USFBN"QQMJDBUJPOz





    ʜ

    View Slide


  27. w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide


  28. Source ͔Β Sink ·Ͱ
    ͭͳ͕ͬͨάϥϑΛ࣮ߦͰ͖Δ͜ͱ͸
    Θ͔ͬͨ
    ͜͜·Ͱ΍ͬͯΈͯ

    View Slide


  29. ֎ͷੈք
    ͭ·ΓΫϥΠΞϯτͱͭͳ͛ͳ͍͔ʁ
    ͜͜·Ͱ΍ͬͯΈͯ

    View Slide

  30. ֎෦͔ΒͷΞΫηεʹର͢ΔॲཧΛετϦʔϜͰ
    8FC4PDLFU͕࢖͑ͦ͏
    ΫϥΠΞϯτͱαʔόͷ૒ํ޲௨৴Λ࣮ݱ͢ΔͨΊͷن֨ɻ
    σʔλૹड৴ʹ͓͍ͯετϦʔϜॲཧͱ૬ੑ͕ྑͦ͞͏ɻ
    1MBZͰαϙʔτ͞Ε͍ͯΔ ਆ

    View Slide

  31. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢
    package controllers
    import javax.inject.Inject
    import play.api.mvc._
    class WebSocketController @Inject()(cc: ControllerComponents) extends
    AbstractController(cc) {
    def connect() = WebSocket.accept {
    // ͜͜ʹԿΛॻ͚͹Α͍͔
    }
    }

    View Slide

  32. 8FC4PDLFUBDDFQUͷఆٛΛݟͯΈΔ
    /**
    * Accepts a WebSocket using the given flow.
    */
    def accept[In, Out](f: RequestHeader => Flow[In, Out, _])(implicit
    transformer: MessageFlowTransformer[In, Out]): WebSocket

    View Slide

  33. 8FC4PDLFUBDDFQUͷఆٛΛݟͯΈΔ
    /**
    * Accepts a WebSocket using the given flow.
    */
    def accept[In, Out](f: RequestHeader => Flow[In, Out, _])(implicit
    transformer: MessageFlowTransformer[In, Out]): WebSocket
    "LLB4USFBNTͷ
    'MPXΛཁٻ͍ͯ͠Δʂ

    View Slide

  34. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢
    package controllers
    import javax.inject.Inject
    import akka.stream.scaladsl.Flow
    import play.api.mvc._
    class WebSocketController @Inject()(cc: ControllerComponents) extends
    AbstractController(cc) {
    def connect() = WebSocket.accept[String, String] { req =>
    Flow.fromFunction(txt => s"flow passed! $txt")
    }
    }

    View Slide

  35. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢
    package controllers
    import javax.inject.Inject
    import akka.stream.scaladsl.Flow
    import play.api.mvc._
    class WebSocketController @Inject()(cc: ControllerComponents) extends
    AbstractController(cc) {
    def connect() = WebSocket.accept[String, String] { req =>
    Flow.fromFunction(txt => s"flow passed! $txt")
    }
    }

    View Slide

  36. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢
    package controllers
    import javax.inject.Inject
    import akka.stream.scaladsl.Flow
    import play.api.mvc._
    class WebSocketController @Inject()(cc: ControllerComponents) extends
    AbstractController(cc) {
    def connect() = WebSocket.accept[String, String] { req =>
    Flow.fromFunction(txt => s"flow passed! $txt")
    }
    }

    View Slide

  37. 1MBZͱ"LLB4USFBNTΛ࢖ͬͯ
    WebSocket ͷόοΫΤϯυΛ
    ߏஙͰ͖Δ͜ͱ͕Θ͔ͬͨʂ

    View Slide

  38. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide


  39. WebSocket ͱ Akka Streams
    Λ࢖ͬͯɺԿ͔ͭ͘Γ͍ͨ

    View Slide


  40. ͻͱΓͷཁٻʹରͯ͠
    ετϦʔϜॲཧΛߦ͏ͷ͸
    REST ͱͦΜͳʹมΘΒͳ͍

    View Slide


  41. ଟରଟ
    ϦΞϧλΠϜ௨৴Λ࣮ݱ͢Δ
    αʔόΛ࣮૷Ͱ͖ͳ͍͔ʁ

    View Slide

  42. ͭ·Γ͜ΜͳΠϝʔδ

    View Slide

  43. ͭ·Γ͜ΜͳΠϝʔδ
    Publish / Subscribe
    νϟωϧΛ࡞Γ͍ͨʂ

    View Slide

  44. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    ͜͜Ͱ՝୊͕ੜ͡Δ
    "LLB4USFBNTͱ͸ʮ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δήʔ
    ϜʯͩͬͨɻઌఔͷྫͰ͸8FC4PDLFUͰ'MPXʹܨ͍ͩॠ
    ؒɺάϥϑ͕׬੒͢Δɻͭ·Γʜ

    View Slide

  45. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    ͜͜Ͱ՝୊͕ੜ͡Δ
    "LLB4USFBNTͱ͸ʮ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δήʔ
    ϜʯͩͬͨɻઌఔͷྫͰ͸8FC4PDLFUͰ'MPXʹܨ͍ͩॠ
    ؒɺάϥϑ͕׬੒͢Δɻͭ·Γʜ

    View Slide

  46. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    ͜͜Ͱ՝୊͕ੜ͡Δ
    "LLB4USFBNTͱ͸ʮ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δήʔ
    ϜʯͩͬͨɻઌఔͷྫͰ͸8FC4PDLFUͰ'MPXʹܨ͍ͩॠ
    ؒɺάϥϑ͕׬੒͢Δɻͭ·Γʜ

    View Slide

  47. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    ͜͜Ͱ՝୊͕ੜ͡Δ
    "LLB4USFBNTͱ͸ʮ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δήʔ
    ϜʯͩͬͨɻઌఔͷྫͰ͸8FC4PDLFUͰ'MPXʹܨ͍ͩॠ
    ؒɺάϥϑ͕׬੒͢Δɻͭ·Γʜ

    View Slide

  48. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    ͜͜Ͱ՝୊͕ੜ͡Δ
    "LLB4USFBNTͱ͸ʮ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δήʔ
    ϜʯͩͬͨɻઌఔͷྫͰ͸8FC4PDLFUͰ'MPXʹܨ͍ͩॠ
    ؒɺάϥϑ͕׬੒͢Δɻͭ·Γʜ
    ͭͳ͍ͩਓ͝ͱʹ
    άϥϑ͕Ͱ͖Δ͚ͩ
    ʗ ?P?
    ʘ

    View Slide

  49. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼

    View Slide

  50. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼

    View Slide

  51. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼

    View Slide

  52. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼
    !?

    View Slide

  53. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼

    View Slide

  54. "LLB4USFBNTͰ1VCMJTI4VCTDSJCFνϟωϧ
    զʑ͕ཉ͍͠෦඼
    8FC4PDLFUͰ઀ଓ͢Δ͝ͱʹ*O0VU͕૿͑ɺͳ͓͔ͭಉ͡
    'MPXʹ઀ଓ͍ͯ͠ΔΫϥΠΞϯτʹϒϩʔυΩϟετͰ͖Δ෦඼
    !!

    View Slide


  55. MergeHub
    BroadcastHub

    View Slide


  56. MergeHub
    BroadcastHub
    are God

    View Slide

  57. .FSHF)VC#SPBEDBTU)VC
    .FSHF)VC
    ".FSHF)VCBMMPXTUPJNQMFNFOUBEZOBNJDGBOJO
    KVODUJPOQPJOUJOBHSBQIXIFSFFMFNFOUTDPNJOHGSPN
    EJ⒎FSFOUQSPEVDFSTBSFFNJUUFEJOB'JSTU$PNFT'JSTU
    4FSWFEGBTIJPO
    #SPBEDBTU)VC
    "#SPBEDBTU)VCDBOCFVTFEUPDPOTVNFFMFNFOUTGSPN
    BDPNNPOQSPEVDFSCZBEZOBNJDTFUPGDPOTVNFST
    Dynamic stream handling • Akka Documentation http://doc.akka.io/docs/akka/current/scala/stream/stream-dynamic.html

    View Slide

  58. .FSHF)VC#SPBEDBTU)VC
    .FSHF)VC
    ".FSHF)VCBMMPXTUPJNQMFNFOUBEZOBNJDGBOJO
    KVODUJPOQPJOUJOBHSBQIXIFSFFMFNFOUTDPNJOHGSPN
    EJ⒎FSFOUQSPEVDFSTBSFFNJUUFEJOB'JSTU$PNFT'JSTU
    4FSWFEGBTIJPO
    #SPBEDBTU)VC
    "#SPBEDBTU)VCDBOCFVTFEUPDPOTVNFFMFNFOUTGSPN
    BDPNNPOQSPEVDFSCZBEZOBNJDTFUPGDPOTVNFST
    Dynamic stream handling • Akka Documentation http://doc.akka.io/docs/akka/current/scala/stream/stream-dynamic.html

    View Slide

  59. .FSHF)VC#SPBEDBTU)VC
    .FSHF)VC
    ".FSHF)VCBMMPXTUPJNQMFNFOUBEZOBNJDGBOJO
    KVODUJPOQPJOUJOBHSBQIXIFSFFMFNFOUTDPNJOHGSPN
    EJ⒎FSFOUQSPEVDFSTBSFFNJUUFEJOB'JSTU$PNFT'JSTU
    4FSWFEGBTIJPO
    #SPBEDBTU)VC
    "#SPBEDBTU)VCDBOCFVTFEUPDPOTVNFFMFNFOUTGSPN
    BDPNNPOQSPEVDFSCZBEZOBNJDTFUPGDPOTVNFST
    Dynamic stream handling • Akka Documentation http://doc.akka.io/docs/akka/current/scala/stream/stream-dynamic.html

    View Slide

  60. .FSHF)VC#SPBEDBTU)VC
    .FSHF)VC
    ".FSHF)VCBMMPXTUPJNQMFNFOUBEZOBNJDGBOJO
    KVODUJPOQPJOUJOBHSBQIXIFSFFMFNFOUTDPNJOHGSPN
    EJ⒎FSFOUQSPEVDFSTBSFFNJUUFEJOB'JSTU$PNFT'JSTU
    4FSWFEGBTIJPO
    #SPBEDBTU)VC
    "#SPBEDBTU)VCDBOCFVTFEUPDPOTVNFFMFNFOUTGSPN
    BDPNNPOQSPEVDFSCZBEZOBNJDTFUPGDPOTVNFST
    Dynamic stream handling • Akka Documentation http://doc.akka.io/docs/akka/current/scala/stream/stream-dynamic.html

    View Slide

  61. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    package controllers
    import javax.inject.Inject
    import akka.actor.ActorSystem
    import akka.stream.scaladsl.{ BroadcastHub, Flow, Keep, MergeHub, Sink }
    import akka.stream.{ ActorMaterializer, KillSwitches, UniqueKillSwitch }
    import play.api.mvc._
    import scala.concurrent.duration._
    class WebSocketController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
    def connect() = WebSocket.accept[String, String] { req =>
    Flow.fromFunction(txt => s"flow passed! $txt")
    }
    def dynamic() = WebSocket.accept[String, String] { req =>
    DynamicStream.publishSubscribeFlow
    }
    }

    View Slide

  62. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    package controllers
    import javax.inject.Inject
    import akka.actor.ActorSystem
    import akka.stream.scaladsl.{ BroadcastHub, Flow, Keep, MergeHub, Sink }
    import akka.stream.{ ActorMaterializer, KillSwitches, UniqueKillSwitch }
    import play.api.mvc._
    import scala.concurrent.duration._
    class WebSocketController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
    def connect() = WebSocket.accept[String, String] { req =>
    Flow.fromFunction(txt => s"flow passed! $txt")
    }
    def dynamic() = WebSocket.accept[String, String] { req =>
    DynamicStream.publishSubscribeFlow
    }
    }
    1VCMJTI4VCTDSJCF
    'MPX͕ཉ͍͠

    View Slide

  63. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  64. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    .FSHF)VCͱ
    #SPBEDBTU)VCΛͭͳ͍Ͱ
    SVO͢Δͱ
    4JOLͱ4PVSDF͕Ͱ͖Δ

    View Slide

  65. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  66. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  67. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  68. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    RUN

    View Slide

  69. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  70. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  71. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    ͭ·Γ͜ΕΛͭͳ͛͹…ʁ

    View Slide

  72. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  73. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    4USFBN͕
    ٧·Βͳ͍Α͏ʹ
    ഉਫޱΛͭͳ͙

    View Slide

  74. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  75. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  76. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  77. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    ͭͳ͍Ͱ'MPXΛ࡞Δ
    cc
    ͜Ε͕ϝοηʔδόεʹ

    View Slide

  78. ࣮ߦ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }

    View Slide

  79. ࣮ߦ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    XBEBͷೖྗ͕

    View Slide

  80. ࣮ߦ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    ZVTVLFͷग़ྗʹʂ

    View Slide

  81. ࣮ߦ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    ZVTVLFͷೖྗ͕

    View Slide

  82. ࣮ߦ
    object DynamicStream {
    implicit val as = ActorSystem()
    implicit val mat = ActorMaterializer()
    lazy val publishSubscribeFlow: Flow[String, String, UniqueKillSwitch] = {
    val (sink, source) =
    MergeHub.source[String](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    val busFlow: Flow[String, String, UniqueKillSwitch] =
    Flow.fromSinkAndSource(sink, source)
    .joinMat(KillSwitches.singleBidi[String, String])(Keep.right)
    .backpressureTimeout(3.seconds)
    busFlow
    }
    }
    XBEBͷग़ྗʹʂ

    View Slide


  83. ͋Εʁ

    View Slide


  84. ɹ͜ΕνϟοτγεςϜ͕Ͱ͖ͨͷͰ͸ʁ

    View Slide

  85. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide

  86. ΋͏΄ͱΜͲͰ͖ͨ΋ಉવ
    1VCMJTI4VCTDSJCFνϟωϧΛ࡞Δ͜ͱ͕Ͱ͖ͨ
    ͜ͷ෦඼ΛԠ༻༷͠ʑͳϦΞϧλΠϜαʔόΛͭ͘Δ
    ࢓૊Έ͕Ͱ͖͍ͯΔͷͰߟ͑Δ͜ͱ͸ߜΒΕΔ
    ԿΛྲྀ͔͢
    Ͳ͏ྲྀ͔͢

    View Slide

  87. ΋͏΄ͱΜͲͰ͖ͨ΋ಉવ
    1VCMJTI4VCTDSJCFνϟωϧΛ࡞Δ͜ͱ͕Ͱ͖ͨ
    ͜ͷ෦඼ΛԠ༻༷͠ʑͳϦΞϧλΠϜαʔόΛͭ͘Δ
    ࢓૊Έ͕Ͱ͖͍ͯΔͷͰߟ͑Δ͜ͱ͸ߜΒΕΔ
    ԿΛྲྀ͔͢νϟοτςΩετɺࢀՃɺ཭୤
    Ͳ͏ྲྀ͔͢ೖྗΛͦͷ··ग़ྗʹྲྀ͚ͩ͢

    View Slide


  88. DEMO

    View Slide


  89. DEMO

    View Slide


  90. DEMO

    View Slide


  91. DEMO

    View Slide


  92. DEMO

    View Slide

  93. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ
    class ChatController @Inject()(
    implicit val system: ActorSystem,
    implicit val materializer: Materializer,
    streamChatService: ChatService
    ) {
    def start(roomId: String) = WebSocket.accept[JsValue, JsValue] { request =>
    val userName = request.queryString("user_name").headOption.getOrElse("anon")
    val userInput: Flow[JsValue, ChatMessage, _] = ActorFlow.actorRef[JsValue, ChatMessage]
    (out => ChatRequestActor.props(out, userName))
    val room = streamChatService.start(roomId, userName)
    val userOutPut: Flow[ChatMessage, JsValue, _] = ActorFlow.actorRef[ChatMessage, JsValue]
    (out => ChatResponseActor.props(out,userName))
    userInput.viaMat(room.bus)(Keep.right).viaMat(userOutPut)(Keep.right)
    }
    }

    View Slide

  94. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ
    class ChatController @Inject()(
    implicit val system: ActorSystem,
    implicit val materializer: Materializer,
    streamChatService: ChatService
    ) {
    def start(roomId: String) = WebSocket.accept[JsValue, JsValue] { request =>
    val userName = request.queryString("user_name").headOption.getOrElse("anon")
    val userInput: Flow[JsValue, ChatMessage, _] = ActorFlow.actorRef[JsValue, ChatMessage]
    (out => ChatRequestActor.props(out, userName))
    val room = streamChatService.start(roomId, userName)
    val userOutPut: Flow[ChatMessage, JsValue, _] = ActorFlow.actorRef[ChatMessage, JsValue]
    (out => ChatResponseActor.props(out,userName))
    userInput.viaMat(room.bus)(Keep.right).viaMat(userOutPut)(Keep.right)
    }
    }
    8FC4PDLFU*/+40/
    ˣม׵
    $IBOOFM*/$IBU.FTTBHF

    View Slide

  95. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ
    class ChatController @Inject()(
    implicit val system: ActorSystem,
    implicit val materializer: Materializer,
    streamChatService: ChatService
    ) {
    def start(roomId: String) = WebSocket.accept[JsValue, JsValue] { request =>
    val userName = request.queryString("user_name").headOption.getOrElse("anon")
    val userInput: Flow[JsValue, ChatMessage, _] = ActorFlow.actorRef[JsValue, ChatMessage]
    (out => ChatRequestActor.props(out, userName))
    val room = streamChatService.start(roomId, userName)
    val userOutPut: Flow[ChatMessage, JsValue, _] = ActorFlow.actorRef[ChatMessage, JsValue]
    (out => ChatResponseActor.props(out,userName))
    userInput.viaMat(room.bus)(Keep.right).viaMat(userOutPut)(Keep.right)
    }
    }
    $IBOOFM065$IBU.FTTBHF
    ˣม׵
    8FC4PDLFU065+40/

    View Slide

  96. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ
    class ChatController @Inject()(
    implicit val system: ActorSystem,
    implicit val materializer: Materializer,
    streamChatService: ChatService
    ) {
    def start(roomId: String) = WebSocket.accept[JsValue, JsValue] { request =>
    val userName = request.queryString("user_name").headOption.getOrElse("anon")
    val userInput: Flow[JsValue, ChatMessage, _] = ActorFlow.actorRef[JsValue, ChatMessage]
    (out => ChatRequestActor.props(out, userName))
    val room = streamChatService.start(roomId, userName)
    val userOutPut: Flow[ChatMessage, JsValue, _] = ActorFlow.actorRef[ChatMessage, JsValue]
    (out => ChatResponseActor.props(out,userName))
    userInput.viaMat(room.bus)(Keep.right).viaMat(userOutPut)(Keep.right)
    }
    }

    View Slide

  97. จࣈνϟοτΛ"LLB4USFBNTͰ࣮૷ͨ͠ײ૝
    1VCMJTI4VCTDSJCFνϟωϧ΄΅ͦͷ··࢖͑Δ
    8FC4PDLFUͰͭͳ͕Δ֎ͷੈք +40/
    ͱɺνϟωϧ
    Ͱྲྀ͢ܕ $IBU.FTTBHF
    Λม׵͢Δ෦඼ 'MPX
    Λ௥
    Ճ͢Δ͜ͱͰνϟωϧ಺ͷܕΛอূͰ͖Δ
    ϑϩϯτΤϯυͷϦΞΫςΟϒϥΠϒϥϦͱ૬ੑ͕ྑ͍
    ࠓճ͸"OHVMBS3Y+4Λར༻

    View Slide

  98. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide

  99. ΄ͱΜͲจࣈνϟοτͱಉ͡ͱࢥ͍͖΍ʜ
    lਓz͝ͱʹνϟωϧ͕ඞཁʹͳΔ
    จࣈνϟοτ͸͢΂ͯͷσʔλΛͻͱͭͷίϯϙʔωϯτʹ
    ཏྻ͍͚ͯ͠͹Α͔͕ͬͨɺϏσΦσʔλ͸Ϣʔβݻ༗͔ͩ
    Βɻ
    ϝϯόʔදͷ؅ཧ͕৽ͨʹඞཁ
    όΠφϦσʔλͷऔಘ͓ΑͼσίʔυʢΫϥΠΞϯτʣ

    View Slide

  100. ϏσΦνϟοτߏ੒֓؍

    View Slide

  101. ϏσΦνϟοτߏ੒֓؍
    ϒϥ΢βʹ͸ࢀՃऀ͝ͱͷWJEFPλά

    View Slide

  102. ϏσΦνϟοτߏ੒֓؍
    αʔόʔαΠυ͸ࢀՃऀ͝ͱͷ
    όΠφϦνϟωϧ

    View Slide

  103. ϏσΦνϟοτߏ੒֓؍
    ࢀՃऀ৘ใ͕ྲྀΕΔผͷνϟωϧ
    ϒϥ΢β։͍ͨΒࢀՃɺ
    ดͨ͡Β཭୤

    View Slide

  104. ΄ͱΜͲจࣈνϟοτͱಉ͡ͱࢥ͍͖΍ʜ
    lਓz͝ͱʹνϟϯωϧ͕ඞཁʹͳΔ
    จࣈνϟοτ͸͢΂ͯͷσʔλΛͻͱͭͷίϯϙʔωϯτʹ
    ཏྻ͍͚ͯ͠͹Α͔͕ͬͨɺϏσΦσʔλ͸Ϣʔβݻ༗͔ͩ
    Βɻ
    ϝϯόʔදͷ؅ཧ͕৽ͨʹඞཁ
    όΠφϦσʔλͷऔಘ͓ΑͼσίʔυʢΫϥΠΞϯτʣ

    View Slide

  105. )5.-.FEJB4PVSDF&YUFOTJPOT
    όΠφϦσʔλΛϒϥ΢β"1*ܦ༝Ͱૢ࡞Ͱ͖Δ

    View Slide

  106. )5.-.FEJB4PVSDF&YUFOTJPOT
    όΠφϦσʔλΛϒϥ΢β"1*ܦ༝Ͱૢ࡞Ͱ͖Δ
    ίϨΛνϟωϧʹྲྀ͢

    View Slide

  107. ͲΜͳ΋ͷΛ࡞͔ͬͨ
    DEMO

    View Slide

  108. ͲΜͳ΋ͷΛ࡞͔ͬͨ
    -PDBM4USFBN
    DEMO

    View Slide

  109. ͲΜͳ΋ͷΛ࡞͔ͬͨ
    νϟωϧΛհͯ͠౉͖ͬͯͨ
    ࣗ෼ͷόΠφϦ
    DEMO

    View Slide

  110. ͲΜͳ΋ͷΛ࡞͔ͬͨ
    νϟωϧΛհͯ͠౉͖ͬͯͨ
    4͞ΜͷόΠφϦ
    DEMO

    View Slide

  111. ͲΜͳ΋ͷΛ࡞͔ͬͨ
    νϟωϧΛհͯ͠౉͖ͬͯͨ
    ,͞ΜͷόΠφϦ
    DEMO

    View Slide

  112. ϏσΦνϟοτΛ"LLB4USFBNTͰ࣮૷ͨ͠ײ૝
    1VCMJTI4VCTDSJCFνϟωϧ΄΅ͦͷ··࢖͑Δ
    ϑϩϯτΤϯυͷϦΞΫςΟϒϥΠϒϥϦͱ૬ੑ͕ྑ͍
    ϝϯόʔͷ૿ݮʹԠͯ͡ಈతʹWJEFPλάΛ௥Ճɻ
    ΫϥΠΞϯταΠυͷ΄͏͕େม͕ͩͬͨɺͦΕͰ΋
    .FEJB4PVSDF&YUFOTJPOTͰָ͕Ͱ͖ͨ΄͏
    8FC35$ͱ͸ผͷܗͰଟରଟͷϏσΦνϟοτΛ࣮૷
    Ͱ͖Δ͜ͱ͕Θ͔ͬͨ

    View Slide

  113. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ
    NJO

    View Slide

  114. ΦϯϥΠϯϚϧόπήʔϜΛ࡞Δ͜ͱʹͨ͠
    ήʔϜͷঢ়ଶΛӬଓԽ
    3FEJTʹ+40/ͱͯ͠อଘ͢Δɻ
    lίϚϯυzͰήʔϜঢ়ଶΛߋ৽ )551

    ͨͩσʔλΛྲྀ͚ͩ͢Ͱͳ͘ɺϩδοΫΛద༻͢Δɻ
    ӬଓԽͨ͠ঢ়ଶΛҰఆִؒͰૹग़
    ྲྀΕͯ͘ΔσʔλΛͦͷ··ར༻͢Δ͜Ε·Ͱͱ͸ҟͳΔɻ

    View Slide

  115. ήʔϜαʔόུ֓ਤ
    ϓϨΠϠʔ͸)551Ͱ$PNNBOEΛૹ৴͢Δ
    ήʔϜαʔό͸ήʔϜϩδοΫΛద༻͠3FEJTΛߋ৽͢Δ
    ήʔϜαʔό͸ҰఆִؒͰ3FEJTͷ(BNF4UBUVTΛૹग़͢Δ

    View Slide

  116. ϓϨΠϠʔ͸)551Ͱ$PNNBOEΛૹ৴͢Δ
    ήʔϜαʔό͸ήʔϜϩδοΫΛద༻͠3FEJTΛߋ৽͢Δ
    ήʔϜαʔό͸ҰఆִؒͰ3FEJTͷ(BNF4UBUVTΛૹग़͢Δ
    ήʔϜαʔόུ֓ਤ
    νϟωϧΛհ͢͜ͱͰ
    ͢΂ͯͷϢʔβ͕
    ಉ͡ήʔϜঢ়ଶΛड৴Ͱ͖Δ

    View Slide


  117. DEMO

    View Slide


  118. DEMO

    View Slide

  119. ήʔϜαʔό࣮૷্ͷϙΠϯτ
    // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)

    View Slide

  120. // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)
    ήʔϜαʔό࣮૷্ͷϙΠϯτ
    'SBNFϝοηʔδΛҰఆִؒͰൃੜͤ͞Δ

    View Slide

  121. // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)
    ήʔϜαʔό࣮૷্ͷϙΠϯτ
    ྲྀΕͯ͘Δϝοηʔδʹର
    ͯ͠ॲཧΛద༻ NBQ

    View Slide

  122. // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)
    ήʔϜαʔό࣮૷্ͷϙΠϯτ
    'SBNFͱ͍͏ϝοηʔδͩͬͨΒɺ
    3FEJT͔Βঢ়ଶΛऔಘͯ͠ૹग़͢Δ

    View Slide

  123. ήʔϜαʔό࣮૷্ͷϙΠϯτ
    // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)

    View Slide

  124. ήʔϜαʔό࣮૷্ͷϙΠϯτ
    // Create bus parts.
    val (sink, source) =
    MergeHub.source[GameMessage](perProducerBufferSize = 16)
    .toMat(BroadcastHub.sink(bufferSize = 1024))(Keep.both)
    .run()
    source.runWith(Sink.ignore)
    sink.runWith(Source.repeat(Frame).throttle(1, 1.second, 5, ThrottleMode.Shaping))
    val channel = GameChannel(sink, source)
    val bus: Flow[GameMessage, GameMessage, UniqueKillSwitch] =
    Flow.fromSinkAndSource(channel.sink, channel.source)
    .joinMat(KillSwitches.singleBidi[GameMessage, GameMessage])(Keep.right)
    .backpressureTimeout(3.seconds)
    .map[GameMessage] {
    case j: Join =>
    gameLogic.join(j)
    cacheStore.find(roomId).get
    case Frame =>
    cacheStore.find(roomId).get
    }
    GameRoom(roomId, bus)
    Ұఆִؒ͝ͱʹήʔϜঢ়ଶ͕ಘΒΕΔ
    ΫϥΠΞϯτͱͯ͠͸
    ͜ΕΛ࢖ͬͯը໘Λඳը͢Ε͹0,

    View Slide

  125. ࿩͢಺༰
    w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α
    w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏
    w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏
    w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼
    w จࣈνϟοτΛ࡞ͬͯΈΑ͏
    w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏
    w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏
    w ·ͱΊ NJO

    View Slide

  126. ϦΞϧλΠϜαʔόʹ͓͍ͯ"LLB͸Ͳ͏͔
    4USFBNTͰ1VCMJTI4VCTDSJCFߏஙՄೳ
    .FSHF)VCͱ#SPBEDBTU)VCͰ8FC4PDLFUͷೖग़ྗΛ
    ಈతʹͭͳ͙͜ͱ͕Ͱ͖ΔɻଟରଟͷϦΞϧλΠϜαʔόߏ
    ஙύλʔϯͷͻͱͭɻ
    ྲྀྔ΍σʔλൃੜස౓ͷௐ੔͕Մೳ
    ཁ݅ʹԠͯ͡ෛՙͷ੍ޚ͕Մೳɻ͜Ε͸"LLB4USFBNTඪ
    ४ػೳͱͯ͠උΘ͍ͬͯΔɻ

    View Slide

  127. ϦΞϧλΠϜαʔόʹ͓͍ͯ4DBMB͸Ͳ͏͔
    ྲྀΕΔϝοηʔδʹܕ৘ใΛ࣋ͨͤΔ͜ͱ͕Ͱ͖Δ
    *OQVU͕ԿͰɺ0VUQVU͕Կͷ෦඼ͳͷ͔໌Β͔ɻܕ৘ใ͕
    ෆਖ਼ͳ෦඼Λ૊Έ߹ΘͤΑ͏ͱ͢ΔͱίϯύΠϧΤϥʔʹͳ
    ΔɻϥϯλΠϜΤϥʔͰ͸ͳ͍ɻ
    ύλʔϯϚονʹΑΔೖग़ྗͷ੍ޚ
    σʔλͷγϦΞϥΠζɺσγϦΞϥΠζͰ׆༂ɻෆਖ਼ͳσʔ
    λͷ৵ೖΛ๷͙ͱͱ΋ʹɺग़ྗ͢ΔσʔλͷܕΛอূ͢Δɻ

    View Slide


  128. Akka Streams Λ࢖ͬͯ
    ଞʹ΋ͳʹ͔Ͱ͖ͦ͏
    ͥͻ࠙਌ձͳͲͰڭ͍͑ͯͩ͘͞
    Twitter ϦϓͰ΋ @waddy_u

    View Slide

  129. ηογϣϯͰར༻ͨ͠ιʔείʔυ͸ͪ͜Β
    αʔόʔαΠυ"LLB4USFBNT
    EFWFMPQFSTJPNFEJBTFSWFSXFCTPDLFUBLLBTUSFBN

    IUUQTHJUIVCDPNDNXBEBZVTVLFEFWFMPQFSTJP
    USFFNBTUFSNFEJBTFSWFSXFCTPDLFUBLLBTUSFBN
    ΫϥΠΞϯταΠυ"OHVMBS
    EFWFMPQFSTJPBOHVMBSNFEJBTUSFBNBQQ

    IUUQTHJUIVCDPNDNXBEBZVTVLFEFWFMPQFSTJP
    USFFNBTUFSBOHVMBSNFEJBTUSFBNBQQ

    View Slide

  130. ࢀߟॻ੶ɾར༻ͨ͠πʔϧ
    w "LLBIUUQBLLBJP
    w "OHVMBSIUUQTBOHVMBSJP
    w .FEJB4PVSDF&YUFOTJPOTäIUUQTXXXXPSH53NFEJBTPVSDF
    w .FEJB4PVSDFIUUQTEFWFMPQFSNP[JMMBPSHKBEPDT8FC"1*.FEJB4PVSDF
    w 8FC4PDLFUTIUUQTXXXQMBZGSBNFXPSLDPNEPDVNFOUBUJPOY4DBMB8FC4PDLFUTXFCTPDLFUT

    View Slide