適切なノードにメッセージを送信するクラスターシャーディングの仕組み
Akkaクラスターには関連するクラスターシャーディングというモジュールがあり、組み合わせて使うことがAkkaのベストプラクティスとされています。クラスターはクラスターシャーディングから依存されているので、通常はクラスターの仕組みを先に説明しますが、この記事ではアクターの延長で理解しやすいクラスターシャーディングの仕組みから先に説明しましょう。
仕組みを説明する準備段階として、クラスターシャーディングが必要となる背景を、ロードバランサーを経由したリクエスト処理を例に紹介します。アプリケーションの外部から、ロードバランサーを経由して、クラスター内のあるアクター宛にメッセージが送られたとしましょう。このアクターは、クラスターを構成するノードのどれかに存在するはずですが、ロードバランサーはそれを知りません。

ロードバランサーはアクターが存在するノードがどれなのかを知らずに、どうやってアクターにメッセージを届けるのでしょう? 力技で無理矢理解決しようとすれば、アクターにIDを割り当てる際に
- IDがaaaから始まるアクターはノードA
- IDがbbbから始まるアクターはノードB
といった規則に従わせることができます。ロードバランサーはこの規則性をもとに適切なノード上のアクターにメッセージを届けられますが、とても現実の運用に耐えられる仕組みではありません。多種多様なアクターのID割当規則を設定するのは非常に手間がかかりますし、長時間ノードがダウンしたら、そのノードに割当てられるべきアクターはずっと復元できないままです。さらに、クラスターを構成するノードは時間と共に増減します。
そこで、クラスターシャーディングです。これを使えば、メッセージの送信元は、宛先のアクターがどのノード上に存在するかという情報を知る必要なくメッセージを届けられます。クラスターを構成するノード間のコミュニケーションを通じて、メッセージは宛先のアクターが存在するノードへと送られます。下図を見ればその大まかな動作がわかるでしょう。

以下では、クラスターシャーディングに関する用語とともに、仕組みの説明をします。クラスターシャーディングにとってアクターのIDは、メッセージの送信経路を決定する重要な意味を持ちます。IDによって特定されるアクターを「エンティティ」、そのIDを「エンティティID」と呼びます。複数のエンティティをまとめたグループはシャードと紐づき、シャードもまたIDを持ちます。さらに複数のシャードは各ノードに1つだけ存在するシャード・リージョンに紐づきます。これらが成す階層構造を視覚的に表現すると、以下の図のようになります。

シャード・リージョンは受け取ったメッセージから、シャードIDとエンティティIDを割り出します。シャード・リージョン自身は、メッセージの宛先であるエンティティがどのシャードに属し、そのシャードがどのシャード・リージョンに属すのかわかりません。そこで、シャード・コーディネーターに問い合わせて、目的のシャード・リージョンを特定します。

目的のシャード・リージョンが、別のシャード・リージョンの場合は上図のように、メッセージを受け取ったシャード・リージョン自身の場合は下図のような動作になります。この仕組みによって、メッセージの最初の送信元はどのノードに対してメッセージを送っても、最終的に正しいエンティティにメッセージを送れます。

非常に複雑な仕組みに見えるクラスターシャーディングですが、開発者が書くソースコードは単純になるように工夫されていて、クラスターシャーディングでもメッセージの送信にはtell APIを使います。これはクラスターを一切使わないときと同じです。
ClusterSharding sharding = ClusterSharding.get(actorSystem); EntityTypeKeytypeKey = EntityTypeKey.create(MessageType.class, "EntityName"); EntityRef entityRef = sharding.entityRefFor(typeKey, "entity-id-aaa"); entityRef.tell(message);
クラスター利用の有無にかかわらず、同じtell APIを使えるのがAkkaの設計の優れたところです。Akkaは最初から分散システムを念頭において開発されたので、1台のノードでもクラスターでも、メッセージの送り方は共通しているのです。そして上記のソースコードで、ノードを意識することなく、エンティティIDだけで送信先が特定できることに注目してください。ノードの情報、例えばそのIPアドレスを意識せずメッセージを送信できることは、Akkaを使ったプログラミングを楽にしてくれます。
クラスターシャーディングを使えば、あるIDを持つアクターは、クラスター内に基本的に1つだけ存在します。では、そのアクターが存在するノードがダウンしたらどうなるでしょう? クラスターがノードのダウンを検知した場合、そのアクターを別のノードで復元します。その際に前回説明したイベント・ソーシングを用いれば、永続化した履歴から内部状態を復元できます。
クラスターシャーディングはこのように良くできた仕組みなのですが、ノードのダウンを検知するというのは、実は非常に奥が深く、現実の世界では多くの人が想像する以上に困難なことです。ここから先は、ノードのダウンを入り口に、分散システムの理論を紹介します。
