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のどのような機能を組み合わせて実現しているかお話します。

664b6e8ebe272fcfa5dbd6070eaf3cd4?s=128

Yusuke Wada

July 29, 2017
Tweet

Transcript

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

  2. ࣗݾ঺հ  w ࿨ా༞հ w ೥݄೔ w ۀ຿4DBMB 1MBZ w

    5XJUUFS!XBEEZ@V w झຯ૲໺ٿ
 ɹɹɹΠΧͷ΍ͭ ৽ࢀ
  3. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

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

    จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  5. "LLBͪΌΜͷಛ௃ฒྻ෼ࢄॲཧϥΠϒϥϦ܈  "DUPST $MVTUFS 1FSTJTUFODF 4USFBNT )551   

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

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

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

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

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

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

    ஙͰ͖Δͱ৭ʑͱԠ༻͕ޮ͘ͷͰ ͸ʂʁ  ྲྀΕདྷͯΔ Google Trend “Akka Streams” ͔ͳΓΠέͯΔϥΠϒϥϦͳͷ͸ ؒҧ͍ͳ͍Μ͚ͩΕͲ΋
  12. "LLBͪΌΜͷಛ௃ͱ͖ͬͭʹ͍͘  ӳޠ͹͔ͬ΍Μ͚ʂ http://doc.akka.io/docs/akka/current/scala/guide/introduction.html http://freecontent.manning.com/akka-in-action-why-use-clustering/

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

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

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

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

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

  18.  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼ w

    จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  19. ετϦʔϜॲཧͷߟ͑ํ  ҰൠతʹετϦʔϜͱ͍͑͹ σʔλൃੜݯ 4PVSDF  σʔλड৴ऀ 4JOL  σʔλ੍ޚ

    'MPX  ͜ΕΒͷ࢓ࣄΛ΍Δਓ͕͍Ε͹ͳΜͱ͔ͳΔ "LLB4USFBNTʹ͓͍ͯ͸Ͳ͏͍͏ѻ͍͔ʁ
  20. ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞ΔήʔϜ  "LLB4USFBNTͰετϦʔϜॲཧΛఆٛ͢Δ৔߹ɿ  ෦඼Λ૊Έ߹ΘͤͯάϥϑΛ࡞Δɻ  ࡞ͬͨάϥϑΛ࣮ߦ͢Δɻ

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

  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
  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͕ಉࠝ͞Ε͍ͯ·͢
  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() } }
  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() } }
  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      ʜ 
  27.  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼ w

    จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  28.  Source ͔Β Sink ·Ͱ ͭͳ͕ͬͨάϥϑΛ࣮ߦͰ͖Δ͜ͱ͸ Θ͔ͬͨ ͜͜·Ͱ΍ͬͯΈͯ

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

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

  31. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢  package controllers import javax.inject.Inject import play.api.mvc._ class WebSocketController

    @Inject()(cc: ControllerComponents) extends AbstractController(cc) { def connect() = WebSocket.accept { // ͜͜ʹԿΛॻ͚͹Α͍͔ } }
  32. 8FC4PDLFUBDDFQUͷఆٛΛݟͯΈΔ  /** * Accepts a WebSocket using the given

    flow. */ def accept[In, Out](f: RequestHeader => Flow[In, Out, _])(implicit transformer: MessageFlowTransformer[In, Out]): WebSocket
  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Λཁٻ͍ͯ͠Δʂ
  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") } }
  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") } }
  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") } }
  37. 1MBZͱ"LLB4USFBNTΛ࢖ͬͯ  WebSocket ͷόοΫΤϯυΛ ߏஙͰ͖Δ͜ͱ͕Θ͔ͬͨʂ

  38. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

    w จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  39.  WebSocket ͱ Akka Streams Λ࢖ͬͯɺԿ͔ͭ͘Γ͍ͨ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  55.  MergeHub BroadcastHub

  56.  MergeHub BroadcastHub are God

  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
  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
  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
  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
  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 } }
  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͕ཉ͍͠
  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 } }
  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͕Ͱ͖Δ
  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 } }
  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 } }
  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 } }
  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
  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 } }
  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 } }
  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 } } ͭ·Γ͜ΕΛͭͳ͛͹…ʁ
  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 } }
  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͕ ٧·Βͳ͍Α͏ʹ ഉਫޱΛͭͳ͙
  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 } }
  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 } }
  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 } }
  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 ͜Ε͕ϝοηʔδόεʹ
  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 } }
  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ͷೖྗ͕
  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ͷग़ྗʹʂ
  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ͷೖྗ͕
  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ͷग़ྗʹʂ
  83.  ͋Εʁ

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

  85. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

    w จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  86. ΋͏΄ͱΜͲͰ͖ͨ΋ಉવ  1VCMJTI4VCTDSJCFνϟωϧΛ࡞Δ͜ͱ͕Ͱ͖ͨ ͜ͷ෦඼ΛԠ༻༷͠ʑͳϦΞϧλΠϜαʔόΛͭ͘Δ ࢓૊Έ͕Ͱ͖͍ͯΔͷͰߟ͑Δ͜ͱ͸ߜΒΕΔ ԿΛྲྀ͔͢ Ͳ͏ྲྀ͔͢

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

  88.  DEMO

  89.  DEMO

  90.  DEMO

  91.  DEMO

  92.  DEMO

  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) } }
  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
  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/
  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) } }
  97. จࣈνϟοτΛ"LLB4USFBNTͰ࣮૷ͨ͠ײ૝  1VCMJTI4VCTDSJCFνϟωϧ΄΅ͦͷ··࢖͑Δ 8FC4PDLFUͰͭͳ͕Δ֎ͷੈք +40/ ͱɺνϟωϧ Ͱྲྀ͢ܕ $IBU.FTTBHF Λม׵͢Δ෦඼ 'MPX

    Λ௥ Ճ͢Δ͜ͱͰνϟωϧ಺ͷܕΛอূͰ͖Δ ϑϩϯτΤϯυͷϦΞΫςΟϒϥΠϒϥϦͱ૬ੑ͕ྑ͍ ࠓճ͸"OHVMBS 3Y+4Λར༻
  98. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  113. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

    w จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  114. ΦϯϥΠϯϚϧόπήʔϜΛ࡞Δ͜ͱʹͨ͠  ήʔϜͷঢ়ଶΛӬଓԽ 3FEJTʹ+40/ͱͯ͠อଘ͢Δɻ lίϚϯυzͰήʔϜঢ়ଶΛߋ৽ )551  ͨͩσʔλΛྲྀ͚ͩ͢Ͱͳ͘ɺϩδοΫΛద༻͢Δɻ ӬଓԽͨ͠ঢ়ଶΛҰఆִؒͰૹग़ ྲྀΕͯ͘ΔσʔλΛͦͷ··ར༻͢Δ͜Ε·Ͱͱ͸ҟͳΔɻ

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

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

  117.  DEMO

  118.  DEMO

  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)
  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ϝοηʔδΛҰఆִؒͰൃੜͤ͞Δ
  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
  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͔Βঢ়ଶΛऔಘͯ͠ૹग़͢Δ
  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)
  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,
  125. ࿩͢಺༰  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼

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

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

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

  129. ηογϣϯͰར༻ͨ͠ιʔείʔυ͸ͪ͜Β  αʔόʔαΠυ"LLB4USFBNT EFWFMPQFSTJPNFEJBTFSWFSXFCTPDLFUBLLBTUSFBN
 IUUQTHJUIVCDPNDNXBEBZVTVLFEFWFMPQFSTJP USFFNBTUFSNFEJBTFSWFSXFCTPDLFUBLLBTUSFBN ΫϥΠΞϯταΠυ"OHVMBS EFWFMPQFSTJPBOHVMBSNFEJBTUSFBNBQQ
 IUUQTHJUIVCDPNDNXBEBZVTVLFEFWFMPQFSTJP USFFNBTUFSBOHVMBSNFEJBTUSFBNBQQ

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

    w 8FC4PDLFUTIUUQTXXXQMBZGSBNFXPSLDPNEPDVNFOUBUJPOY4DBMB8FC4PDLFUTXFCTPDLFUT