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. ࣗݾ঺հ  w ࿨ా༞հ w ೥݄೔ w ۀ຿4DBMB 1MBZ w

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

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

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

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

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

    of awesome streaming frameworks, applications, etc https://github.com/ manuzhang/awesome-streaming وॏͳετϦʔϛϯάϥΠϒϥϦ
  7.  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼ w

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

    'MPX  ͜ΕΒͷ࢓ࣄΛ΍Δਓ͕͍Ε͹ͳΜͱ͔ͳΔ "LLB4USFBNTʹ͓͍ͯ͸Ͳ͏͍͏ѻ͍͔ʁ
  9. ίʔυϕʔεͰݟ͍͖ͯ·͢ʂ  $ 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
  10. ίʔυϕʔεͰݟ͍͖ͯ·͢ʂ  $ 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͕ಉࠝ͞Ε͍ͯ·͢
  11. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε  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() } }
  12. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε  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() } }
  13. ͷഒ਺Λग़ྗ͢ΔΑ͏ͳάϥϑΛ࡞Ε  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      ʜ 
  14.  w "LLB4USFBNTΛ࢖ͬͯΈͨΜͰ͢Α w άϥϑΛ࡞࣮ͬͯߦͯ͠ΈΑ͏ w 1MBZΛ࢖ͬͯ8FC4PDLFUͷόοΫΤϯυʹ͠Α͏ w Կͭ͘Γ͍ͨʁ1VCMJTI4VCTDSJCFΛࢧ͑Δ෦඼ w

    จࣈνϟοτΛ࡞ͬͯΈΑ͏ w ϏσΦνϟοτΛ࡞ͬͯΈΑ͏ w ΦϯϥΠϯήʔϜαʔόΛ࡞ͬͯΈΑ͏ w ·ͱΊ NJO
  15. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢  package controllers import javax.inject.Inject import play.api.mvc._ class WebSocketController

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

    flow. */ def accept[In, Out](f: RequestHeader => Flow[In, Out, _])(implicit transformer: MessageFlowTransformer[In, Out]): WebSocket
  17. 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Λཁٻ͍ͯ͠Δʂ
  18. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢  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") } }
  19. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢  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") } }
  20. ઌఔͭͬͨ͘ϓϩδΣΫτͰૣ଎ࢼ͢  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") } }
  21. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  22. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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͕ཉ͍͠
  23. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  24. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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͕Ͱ͖Δ
  25. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  26. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  27. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  28. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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
  29. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  30. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  31. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } } ͭ·Γ͜ΕΛͭͳ͛͹…ʁ
  32. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  33. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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͕ ٧·Βͳ͍Α͏ʹ ഉਫޱΛͭͳ͙
  34. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  35. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  36. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 } }
  37. ଟରଟͷςΩετૹड৴νϟωϧΛ࡞ͬͯΈΔ  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 ͜Ε͕ϝοηʔδόεʹ
  38. ࣮ߦ  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 } }
  39. ࣮ߦ  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ͷೖྗ͕
  40. ࣮ߦ  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ͷग़ྗʹʂ
  41. ࣮ߦ  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ͷೖྗ͕
  42. ࣮ߦ  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ͷग़ྗʹʂ
  43. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ  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) } }
  44. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ  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
  45. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ  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/
  46. จࣈνϟοτͷ࣮૷্ͷϙΠϯτ  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) } }
  47. จࣈνϟοτΛ"LLB4USFBNTͰ࣮૷ͨ͠ײ૝  1VCMJTI4VCTDSJCFνϟωϧ΄΅ͦͷ··࢖͑Δ 8FC4PDLFUͰͭͳ͕Δ֎ͷੈք +40/ ͱɺνϟωϧ Ͱྲྀ͢ܕ $IBU.FTTBHF Λม׵͢Δ෦඼ 'MPX

    Λ௥ Ճ͢Δ͜ͱͰνϟωϧ಺ͷܕΛอূͰ͖Δ ϑϩϯτΤϯυͷϦΞΫςΟϒϥΠϒϥϦͱ૬ੑ͕ྑ͍ ࠓճ͸"OHVMBS 3Y+4Λར༻
  48. ήʔϜαʔό࣮૷্ͷϙΠϯτ  // 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)
  49. // 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ϝοηʔδΛҰఆִؒͰൃੜͤ͞Δ
  50. // 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
  51. // 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͔Βঢ়ଶΛऔಘͯ͠ૹग़͢Δ
  52. ήʔϜαʔό࣮૷্ͷϙΠϯτ  // 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)
  53. ήʔϜαʔό࣮૷্ͷϙΠϯτ  // 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,