Akka実践バイブルをゆっくり読み解く　第６章Akkaによるはじめての分散アプリケーション

[Akka実践バイブルをゆっくり読み解く企画](https://qiita.com/louvre2489/items/8a649fa0280d39e144da)の第６章です。
# 第６章 Akkaによるはじめての分散アプリケーション
Akkaを使う動機も色々あるかと思いますが、やはり『分散』とか『スケール』とかいったキーワードは採用の強い動機になるのではないかと思います。この１人読書会も17人の私で分散してまとめられたら一気に終わるのに・・・

# JVM言語の分散処理フレームワークについて
JVM系の世界であればHadoopとかSparkあたりが分散フレームワークとして有名だが、これらとAkkaの違いって何なんでしょう？StackOverFlowで[面白い質問](https://stackoverflow.com/questions/29088754/apache-spark-vs-akka)を見つけました。これのベストアンサーによると、以下のようなイメージ。

- Akkaは大量にやってくるデータを用いたリアルタイム処理が得意
- Sparkは大量に蓄積されたデータを用いたバッチ処理が得意

このあたりを頭の片隅に入れつつ、Akkaの分散処理について見ていきたい。

ちなみに、分散フレームワークについて調べる中で知ったんだけど、[Akkaの誕生日](https://en.wikipedia.org/wiki/Akka_(toolkit))は2009年で[Sparkの誕生日](https://en.wikipedia.org/wiki/Apache_Spark)は2014年らしい。Sparkって意外と若い。[Hadoop](https://en.wikipedia.org/wiki/Apache_Hadoop)ですら2011年生まれということで、Akkaが最年長であるという衝撃の事実を知りました。JVM系の分散フレームワークとしての知名度は確実にHadoop/Sparkの方が有名だと思うんですが、JVM系の分散フレームワークとしてはAkkaの方が先駆者だったようです。

ちなみに、Sparkは内部的にAkkaを利用しているらしい。

> Spark makes use of the Akka toolkit for scheduling jobs between different nodes.

# Akkaによる分散処理
『Akkaによる』というよりは『アクターによる』という方が正確なのかもしれないけど、以下の特徴を持つ。

- 他アクターにメッセージを送信する場合は`ActorRef`を使用する
- ローカルアクセスの場合も、リモートアクセスの場合も、`ActorRef`に対して同じ処理を通してアクセスすることができる
- アクセスだけではなく、アクターの死活監視も同じ方法で行うことができる

# 実際にスケールアウトさせる
ここまではアクターを単一のノードで実行してきたが、ノードは複数作成してアクターの配置を分散させることができる。１サーバ１ノードである必要はなく、１サーバに複数ノードを配置させることもできる。
このとき、リモートノードのアクターへの参照を取得する方法は２つある。

1. パス(URL)でアクターを検索する
2. 生成したアクターの参照をリモートにデプロイする

## パス(URL)でアクターを検索する
書籍に従ってREPLで実行してみる。まずはバックエンドのアクターシステムから。

![範囲を選択_047.png](https://qiita-image-store.s3.amazonaws.com/0/64360/730a2e34-3f6a-7ddd-8654-d2c5d834ff7b.png)

![範囲を選択_048.png](https://qiita-image-store.s3.amazonaws.com/0/64360/06d2ba53-d382-ae4b-b947-e6438bca0402.png)

起動に成功したっぽい。

``` scala
[INFO] [02/24/2018 22:52:53.603] [run-main-0] [akka.remote.Remoting] Starting remoting
[INFO] [02/24/2018 22:52:53.796] [run-main-0] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://backend@0.0.0.0:2551]
[INFO] [02/24/2018 22:52:53.800] [run-main-0] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://backend@0.0.0.0:2551]
```

バックエンドのアクターシステムにアクターを載せる。
![範囲を選択_049.png](https://qiita-image-store.s3.amazonaws.com/0/64360/b8716378-d4df-e527-5964-f6db957bb0f0.png)

次にフロントエンドのアクターシステムを作成する。この時、`sbt`は別ターミナルでもうひとつ立ち上げる必要がある。
![範囲を選択_050.png](https://qiita-image-store.s3.amazonaws.com/0/64360/ef3ec57b-a385-b808-d03f-043479fd3312.png)

フロントエンドのアクターシステムにもアクターを載せる。
![範囲を選択_052.png](https://qiita-image-store.s3.amazonaws.com/0/64360/a5cffc53-2863-4c22-552f-c7b41c98d788.png)

こちらも起動に成功。

``` scala
[INFO] [02/24/2018 23:11:20.072] [run-main-0] [akka.remote.Remoting] Starting remoting
[INFO] [02/24/2018 23:11:20.268] [run-main-0] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://frontend@0.0.0.0:2552]
[INFO] [02/24/2018 23:11:20.270] [run-main-0] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://frontend@0.0.0.0:2552]
```

それでは満を持して動かしてみる。
[メッセージ送信]
![範囲を選択_053.png](https://qiita-image-store.s3.amazonaws.com/0/64360/63066eff-1cd0-220b-9d02-4d75c6e6c5bd.png)
[メッセージ受信]
![範囲を選択_054.png](https://qiita-image-store.s3.amazonaws.com/0/64360/b2518ab4-33e6-fee3-0d96-4b66bed8452c.png)

このようにURLを指定することで異なるノードのアクターにメッセージを送信することができた。実際はフロントエンド/バックエンドを異なるサーバにすることで処理を分散することができる。

## 生成したアクターの参照をリモートにデプロイする
「デプロイ」という名前は付いているけど、リモートサーバに何かを配置するのではなく、リモート上にアクターを生成するイメージ。前提として、参照先サーバのアクターシステムが動いている必要がある。フロントエンド側でアプリケーションを実行した際に、プログラムコードまたは設定ファイルの内容に従ってリモートデプロイを行ってくれる。
以下がその様子。赤枠の部分がフロントエンドからバックエンドにリモートデプロイした事実を示すらしい。
![ワークスペース 1_055.png](https://qiita-image-store.s3.amazonaws.com/0/64360/9405609d-5cdc-52ac-29a5-70d0f811892b.png)

設定ファイルによるリモートデプロイの方法は以下のとおり。
deployment{}の中で設定されたパスを持つアクターがリモートでデプロイされるようになる。

``` scala:confの設定
akka {
  actor {
    provider = "akka.remote.RemoteActorRefProvider"

    deployment {
      /boxOffice {
        remote = "akka.tcp://backend@0.0.0.0:2551"
      }
    }
  }
  ...
}
```

プログラムでのデプロイも可能だが、変更時のビルドが不要であることから設定ファイルでの実施が書籍で推奨されているため、プログラムでのデプロイについては割愛。

## 耐障害性の向上
フロントエンド/バックエンド用のアクターシステムを別々で起動してメッセージのやり取りができるようになったので、これにより２章で見たレッチリのチケット販売システムをフロントエンド/バックエンドに分割することができる。しかし、同一システムで動いていない以上、『参照先のアクターが正しく動いているか』を意識する必要がある。
これについて、以下のように考える必要がある。

### パス(URL)でアクターを検索する場合
リモート参照用の`ActorRef`が、本当にリモート参照可能な状態にあるかを確認する必要がある。

1. 初回通信時は新規リモート参照用の`ActorRef`を生成する
2. それ以降は、1.で取得した`ActorRef`を使用する
3. リモート参照先の`ActorRef`が終了していることを検知できた場合は、新たなリモート参照用の`ActorRef`を生成する

リモート参照の保持状態を`receive`に持たせて、`context.become`によって切り替えることによって実現するらしいが、Akkaのコードを追いかけてもよくわからなかった。何で`receive`の挙動がこれで変わるんだろか・・・？

### 生成したアクターの参照をリモートにデプロイする場合
基本的な発想は上記と同様。
違いは、リモート通信可能な状態にない場合は、リモートデプロイを実施することでリモート参照を可能にすることにある。

# マルチJVMのテスト
処理をスケールさせることによって、テストが複雑になる。複数のJVMで動作するアクター、今回であればフロントエンド/バックエンド用のアクターを制御するための仕組みを`multi-JVM`プラグインが提供してくれる。これによって、「両アクターシステムが起動するまでテストを実行しない」などの同期制御(barrier)を行うことができるようになる。
アウトラインはわかったけど、`sbt`関連の設定が難しかったり、`role`の設定の意味するところがわからなかったり、正直、詳細を理解するにはスキルが不足しすぎなので、今回は詳細まで深入りしないことにする。

# 個人的まとめ
書籍にも書かれているとおり、複数のコア(ローカル/リモート問わず)で処理することが当然の時代となってきた。余程スケールされないことが確定しているシステムを除き、分散処理されることを前提とした抽象化の仕組みが必要であり、そのための仕組みをAkkaが提供してくれる。そのため、『リモート処理をしている箇所』に対して考慮するようにするのではなく、『リモート処理可能である』ことを前提とした考慮を行う。これがAkkaの思想である。