Akka実践バイブルをゆっくり読み解く企画の第6章です。
第6章 Akkaによるはじめての分散アプリケーション
Akkaを使う動機も色々あるかと思いますが、やはり『分散』とか『スケール』とかいったキーワードは採用の強い動機になるのではないかと思います。この1人読書会も17人の私で分散してまとめられたら一気に終わるのに・・・
JVM言語の分散処理フレームワークについて
JVM系の世界であればHadoopとかSparkあたりが分散フレームワークとして有名だが、これらとAkkaの違いって何なんでしょう?StackOverFlowで面白い質問を見つけました。これのベストアンサーによると、以下のようなイメージ。
- Akkaは大量にやってくるデータを用いたリアルタイム処理が得意
- Sparkは大量に蓄積されたデータを用いたバッチ処理が得意
このあたりを頭の片隅に入れつつ、Akkaの分散処理について見ていきたい。
ちなみに、分散フレームワークについて調べる中で知ったんだけど、Akkaの誕生日は2009年でSparkの誕生日は2014年らしい。Sparkって意外と若い。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サーバ1ノードである必要はなく、1サーバに複数ノードを配置させることもできる。
このとき、リモートノードのアクターへの参照を取得する方法は2つある。
- パス(URL)でアクターを検索する
- 生成したアクターの参照をリモートにデプロイする
パス(URL)でアクターを検索する
書籍に従ってREPLで実行してみる。まずはバックエンドのアクターシステムから。
起動に成功したっぽい。
[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]
次にフロントエンドのアクターシステムを作成する。この時、sbt
は別ターミナルでもうひとつ立ち上げる必要がある。
こちらも起動に成功。
[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]
それでは満を持して動かしてみる。
[メッセージ送信]
[メッセージ受信]
このようにURLを指定することで異なるノードのアクターにメッセージを送信することができた。実際はフロントエンド/バックエンドを異なるサーバにすることで処理を分散することができる。
生成したアクターの参照をリモートにデプロイする
「デプロイ」という名前は付いているけど、リモートサーバに何かを配置するのではなく、リモート上にアクターを生成するイメージ。前提として、参照先サーバのアクターシステムが動いている必要がある。フロントエンド側でアプリケーションを実行した際に、プログラムコードまたは設定ファイルの内容に従ってリモートデプロイを行ってくれる。
以下がその様子。赤枠の部分がフロントエンドからバックエンドにリモートデプロイした事実を示すらしい。
設定ファイルによるリモートデプロイの方法は以下のとおり。
deployment{}の中で設定されたパスを持つアクターがリモートでデプロイされるようになる。
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/boxOffice {
remote = "akka.tcp://backend@0.0.0.0:2551"
}
}
}
...
}
プログラムでのデプロイも可能だが、変更時のビルドが不要であることから設定ファイルでの実施が書籍で推奨されているため、プログラムでのデプロイについては割愛。
耐障害性の向上
フロントエンド/バックエンド用のアクターシステムを別々で起動してメッセージのやり取りができるようになったので、これにより2章で見たレッチリのチケット販売システムをフロントエンド/バックエンドに分割することができる。しかし、同一システムで動いていない以上、『参照先のアクターが正しく動いているか』を意識する必要がある。
これについて、以下のように考える必要がある。
パス(URL)でアクターを検索する場合
リモート参照用のActorRef
が、本当にリモート参照可能な状態にあるかを確認する必要がある。
- 初回通信時は新規リモート参照用の
ActorRef
を生成する - それ以降は、1.で取得した
ActorRef
を使用する - リモート参照先の
ActorRef
が終了していることを検知できた場合は、新たなリモート参照用のActorRef
を生成する
リモート参照の保持状態をreceive
に持たせて、context.become
によって切り替えることによって実現するらしいが、Akkaのコードを追いかけてもよくわからなかった。何でreceive
の挙動がこれで変わるんだろか・・・?
生成したアクターの参照をリモートにデプロイする場合
基本的な発想は上記と同様。
違いは、リモート通信可能な状態にない場合は、リモートデプロイを実施することでリモート参照を可能にすることにある。
マルチJVMのテスト
処理をスケールさせることによって、テストが複雑になる。複数のJVMで動作するアクター、今回であればフロントエンド/バックエンド用のアクターを制御するための仕組みをmulti-JVM
プラグインが提供してくれる。これによって、「両アクターシステムが起動するまでテストを実行しない」などの同期制御(barrier)を行うことができるようになる。
アウトラインはわかったけど、sbt
関連の設定が難しかったり、role
の設定の意味するところがわからなかったり、正直、詳細を理解するにはスキルが不足しすぎなので、今回は詳細まで深入りしないことにする。
個人的まとめ
書籍にも書かれているとおり、複数のコア(ローカル/リモート問わず)で処理することが当然の時代となってきた。余程スケールされないことが確定しているシステムを除き、分散処理されることを前提とした抽象化の仕組みが必要であり、そのための仕組みをAkkaが提供してくれる。そのため、『リモート処理をしている箇所』に対して考慮するようにするのではなく、『リモート処理可能である』ことを前提とした考慮を行う。これがAkkaの思想である。