Akka実践バイブルをゆっくり読み解く企画の第1章です。
第1章 Akkaの紹介
この章はタイトルのとおり、Akkaの紹介。
色々説明してくれているけど、第2章以降で手を動かしながら学習した方が効率良さそうなので、ここではキーワードの整理レベルしかしない。簡易にまとめるために(?)書籍の章立ては無視しています。
TODO:最後にしっかり振り返ること!
スケーラビリティと並行性
2000年代中盤に、CPU性能向上が限界に達しつつあることが明らかになってきた。
ムーアの法則の限界ですね。
というわけで、アプリケーションの速度を求める場合はアプリケーションを並行に動作させる必要が出てきた。
アプリケーションを並行に動作させる際のキーワードは以下のとおり。
- スケーラビリティ
- システムがパフォーマンスにマイナスの影響を与えることなく、リソースに対する需要の変化に適応できるという尺度。
- 並行性
- スケーラビリティを達成するための手段。
- スケールアウト
- クラスターに動的にサーバを追加すること。
これらをコストと効率性のバランスで考える必要がある。
アプリケーションのスケール
以下の2点が期待される。
- 需要の増加に合わせて線形もしくはそれより緩やかな速度で必要なリソースを増加させる。
- リソースを増加させる場合は、アプリケーションの複雑さは同じままか、緩やかに増加させることが望ましい。
パワーアップはしても複雑にはなってほしくない。
そりゃそうだよねー。
そこでAkka
Akkaは、並行・分散アプリケーションをシンプルに単一のモデルで実践するプログラミングモデルを提供してくれる。
そのモデルはプログラミングモデルと呼ばれる。
アクターといえばErlang。
Erlangといえばアクター
自分の周りにはErlang使える人はいないし、お仕事の話も聞いたことがない。
利用実態はどんなモンなんだろか・・・
アクターの概要
アクターは、非常に小さく縮小されたプログラマブルなメッセージキューのようなもの。
以下、アクターの特徴。
- 小さいので何百万ものアクターを作ることができる。
- アクターはメッセージが送られない限りは何もしない。
- アクターは全て非同期。
アクターモデル以外を使った場合の例
機能要件/非機能要件が増えてくると、単純に複雑さは増してくる。
特に、複数台構成にしてステートレスにすると、ステートの持ち場が限定されるため、それが更なる複雑性を生み出す。
※不可能だとは言っていない。
後続のアクターモデルの素晴らしさとの退避のために色々な例が記述されているけど、まとめるの大変なので省略。。。
アクターモデルを使うとどうなるのか?
以下のようなことが実現できるようになる。
- ステートをインメモリーで持たせ、かつアプリケーションが再起動された場合でも、状態をログから復元できる。
- 関連イベントが発生した場合は、アクターにイベントをプッシュ通知することができる。
- アクター自体が非同期なので、アクターモデルでは非同期処理を簡易に実現することができる。
- アクター自体が非同期なので、それを前提とした処理にしておけば一部の処理がうまく動かない場合でも他処理への影響を抑えることができる
これらを1つずつ詳細に見ていく。
Akkaによるスケールと耐久性
イベントの内容を保持しておき、再起動の際はそのイベントを前回と同じ順で再現することで前回と同一の状態を生み出す。
DBで言うところのロールフォワード的なことをする。
書籍内でも「ジャーナル」とか「一貫(性)」とか聞き慣れた単語が出てくる。
ただし、1つのサーバ内に全てのイベントを集中的に管理させると、「スケールアウト」させることができなくなってしまうため、何らかの方法でデータを分割して保持する必要がある。
この際の方法として、「シャーディング/パーティショニング」が挙げられる。
ここでもやっぱりDBで聞き慣れた言葉が。。。
Akkaによるスケーリングとインタラクティブな機能
何か関心のあることが起こったとき、アプリケーションの各オブジェクトはイベントを自分で送信する。
ポーリングはブラウザが能動的に状態を監視することになるが、アクターモデルの場合はアクター自身が能動的に状態を通知する。
Akkaでのスケールと障害
処理の一部で障害が発生した場合、業務による影響によって継続の判断をすることができる。
一連の処理の中で障害が発生したとき、業務にとってクリティカルでなければ業務は可能な限り継続させたい。
また、システムをスケールさせるという観点でイベント駆動のアプローチを採用することには、以下のメリットがある。
- コンポーネント間の依存性を最小限にする
- コンポーネント間の処理の時系列を問わない
- コンポーネント間の位置関係を問わない
アクターの特徴
非同期モデル
あるコンポーネントから別のコンポーネントの処理を呼出す際に、応答を待たずに処理を続けることができる。
サーバを複数台構成にした場合に、別サーバからのレスポンスを待ち続けるようになってしまうと
トラブル時に全ての呼び出しがブロックされてしまうため、それを非同期モデルによって回避する。
アクターの操作
アクターは以下の操作を持った軽量プロセス。
操作は全て非同期に行われる。
- 生成
- 送信
- 状態変化
- 監督
送信
オブジェクト指向のカプセル化を更に進めた処理方式。
ここまでで「並列」というキーワードが何回か出てきているが、アクター間では可変な状態を並列的に変更することはできない。
全て不変(イミュータブル)なメッセージでデータをやり取りする。メッセージを編集したい場合は、元のメッセージのコピーを作成し、
別モノとして編集を行い、既存の内容を置き換えることで対応する。
アクターは処理の時間関係を問わずに非同期で処理をするものであるため、複数のユーザが同一メッセージを編集した場合は後勝ちになる。←解釈合ってるのかな???
生成
アクターはアクターを生成することができる。
生成されたアクターは、ジャーナルから状態を復元することで前回と同じ状態を再現する。
状態変化
状態による処理の分岐を実現する。
状態によってメッセージをどのように扱うかを変更することができる。
監督
スーパーバイザー。
システム内のコンポーネントに不具合が発生した場合、どのように振る舞うべきかを決定する。
スーパーバイザーは、どのアクターがどんな理由でクラッシュしたかを示すメッセージを受け取ることができるため、
これを元に、「業務継続可能な問題であれば処理を継続する」という判断をすることもできる。
Akkaのアクター
スーパーバイザーがその他のアクターを統合して大きな1つの仕組み(=アクターシステム)として機能する。
アクターはメッセージボックスを持ち、メッセージボックス内のメッセージを1つずつ処理していく。
メッセージボックス内のメッセージをアクターにプッシュするための仕組みとして、ディスパッチャーというものが存在する。
- メールボックス
- メールボックス≒キュー。
- ActorRefに送信されたメッセージを到着順に処理するための一時保存先。
- ディスパッチャー
- ディスパッチャーの種類によってどのスレッドモデルを使用してメッセージをプッシュするかを決めることができる。
スレッドプールのサイズ違いのスレッドを使い分けたりできるらしい。 - 参考:https://qiita.com/hayasshi/items/12e3e075752eac015446
- ディスパッチャーの種類によってどのスレッドモデルを使用してメッセージをプッシュするかを決めることができる。
- ActorRef
- 文字通り解釈すると、Actorの参照、つまりActorのアドレスを示す。
位置の透過性なんかを実現する仕組み。
- 文字通り解釈すると、Actorの参照、つまりActorのアドレスを示す。
1章まとめ
後半の説明はわかったようなわからないような・・・
どちらかというとわかっていないっぽい。この辺は読み進めていったらわかるようになるのかな?
ここまでScalaのコードは1つたりとも登場しない・・・