Scala
Akka

Akka-Remote使ってみた

Akka-Remoteを使ってみた

なにができるものなの?

  • Actorがサーバをまたいで作成、コミュニケーションできるようになるもの

Actorってそもそもなに?

  • Actor Modelで登場するActor
  • Actor同士は非同期なメッセージでやりとりが行われる
  • 位置透過性を実現することができる <- ここが重要

位置透過性って?

  • Actorが同じマシン上にいても、リモート上にいても同じように扱えること

改めて、なにができるもの?

  • Actorの特徴の位置透過性を実現するためにAkka-Remoteが使えるよ。
  • Akka-Remoteを使えばとっても簡単に特定のActorを別のサーバで動かすことができる

使って見た

まずはローカルで動くActorを作ってみる

  • src/main/resource/application.confを作っておこう
akka {
  actor {
    provider = "local"
  }
}

  • ActorSystemの生成、Actorの生成
Main.scala
object Court {
  def main(args: Array[String]): Unit = {
    val config = ConfigFactory.load("application.conf")
    implicit val timeout: Timeout = Timeout(5000.milliseconds)
    implicit val system: ActorSystem = ActorSystem("Tennis", config)
    val playerA = system.actorOf(Props(classOf[TennisPlayer], "A", "a-!!"), "playerA")
    val playerB = system.actorOf(Props(classOf[TennisPlayer], "B", "un~~!"), "playerB")

    playerA ! GameStart(playerB)

    Thread.sleep(1000)
    system.terminate()
    sys.exit()
  }
}

case class TennisPlayer(name: String, howling: String) extends Actor {
  override def receive: Receive = {
    case Ball(vsPlayer) =>
      stroke(vsPlayer)
    case GameStart(vsPlayer) =>
      serve(vsPlayer)
  }

  def stroke(vsPlayer: ActorRef): Unit = {
    println(s"${name} < ${howling}!")
    vsPlayer ! Ball(self)
  }

  def serve(vsPlayer: ActorRef): Unit = {
    println(s"${name} < serve!!!")
    vsPlayer ! Ball(self)
  }
}

case class Ball(player: ActorRef)

case class GameStart(vsPlayer: ActorRef)


Court.mainを実行するとコンソールにプレイヤーA, Bが交互に叫びます。

A < serve!!!
B < un~~!!
A < a-!!!
B < un~~!!
A < a-!!!
B < un~~!!

別のサーバにActorを移してみた

application.confを修正する

  • providerをremoteに変更しする
  • /playerBのパスに生成されるActorを"akka.tcp://bCourt@127.0.0.1:2553"が指すサーバ上にあるActorSystemで動かすために、 "/playerB"の"remote"に"akka.tcp://bCourt@127.0.0.1:2553"を渡してる
  • この設定をconfに書いておいて、providerremoteであれば
system.actorOf(Props(classOf[TennisPlayer], "B", "un~~!"), "playerB")

system.actorOfで第二引数でpathを指定するときにplayerBにした場合、application.confから勝手に設定を読み込んでActorを生成してくれる

application.conf
akka {
  actor {
    provider = "remote"
    deployment {
      "/playerB" {
        # playerB Actorを動かすパスとポートを指定する
        # この先ではAkka Remoteによってactorのデプロイを受け付けるサーバになっている
        remote = "akka.tcp://bCourt@127.0.0.1:2553" 
      }
    }
  }
}

playerBが動くActorSystemの用意

  • playerAが動くActorSystemがいるサーバとは別のサーバにする
  • bCourtという名でActorSystemを作る
object PlayerBCourt {
  def main(args: Array[String]): Unit = {
    implicit val timeout: Timeout = Timeout(5000.milliseconds)
    val config = ConfigFactory.load("bCourt-application.conf")
    ActorSystem("bCourt", config)
  }
}
  • bCourtのためのbCourt-application.confを作る
akka {
  actor {
    provider = "remote"
  }

  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 2553
    }
  }
}

Actor同士でやりとりをしてみる

  • 二つのターミナルウィンドウを用意してそれぞれBCourt, Courtmainを実行する
  • BCourtではBさんが吠えながらボールを返し、もう一方ではAさんがボールを返します

スクリーンショット 2017-11-28 11.31.48.png

できたね!

  • application.confの akka.actor.providerを localにすれば1つのサーバ上でコンソール出力がされ、remoteにすれば二つのコンソール上から出力がされる
  • ↑の切り替えはapplication.confのakka.actor.providerを localremoteにするだけでできて、ソースコードには変更はなしでできるね
  • ソースコード上ではメッセージを送る相手が、どこにいるのか意識しなくて良いね