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に書いておいて、`provider`が`remote`であれば

```
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`, `Court`の`main`を実行する
- BCourtではBさんが吠えながらボールを返し、もう一方ではAさんがボールを返します

![スクリーンショット 2017-11-28 11.31.48.png](https://qiita-image-store.s3.amazonaws.com/0/153296/3f8245b3-6734-bdc0-47ce-8a56065a664e.png)


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