モジュールとVerticle ~ モジュールクラスローダー
実際にモジュールを作成する前に、Vert.xモジュールが起動される仕組みはどういうものなのか。また、モジュールのmainとなる実行インターフェースのVerticleについて簡単に説明したいと思います。
まず、起動時にはVert.x自身をロードするプラットフォームクラスローダーと呼ばれるクラスローダーを持ちます。そして各モジュールをロードする際に、指定されているモジュール配置ディレクトリ(VERTEX_MODS)配下より、プラットフォームクラスローダーを親とし、各々のモジュール個別にモジュールクラスローダーという独立したクラスローダーを作成してロードします。
プラットフォームクラスローダーは環境変数CLASSPATHで指定されたパスと${VERTX_HOME}/conf、${VERTX_HOME}/lib/*を対象とします。モジュールクラスローダーは、そのモジュールディレクトリ配下のクラス、およびlibディレクトリ配下のjarファイルを対象としてロードします。
下図のcom.bloidonia~mod-jdbc-persistor~2.1.3 、およびその下位のlibディレクトリ配下のjarファイルがモジュールクラスローダーの対象となります。
対象のモジュールが${VERTX_MODS}に存在しない場合、クラスパス内のrepos.txtに指定されているリポジトリから自動的にダウンロードし、${VERTX_MODS}に展開されます。
複数のモジュールを起動する場合は、mod.jsonの"includes" : "…" にモジュール名を記述することで同時起動が可能です。また、各モジュール内で複数のVerticleや別のモジュールを起動(デプロイ)することもできます。
container.deployVerticle("verticleClassName"); // Verticleクラスを起動
container.deployModule("moduleName"); // モジュールを起動
この場合、注意すべきはモジュール内からロードした別モジュール、Verticleはそれぞれ別のモジュールクラスローダーが作成され、プラットフォームクラスローダーとの親子関係ができる点です。
これの意味するところは、モジュールのクラスパス内にのみ存在するクラスでは、同じモジュール内だとしても異なるVerticle間でstaticフィールドの変数値を共有することができないということになります。これは通常のJavaのイメージで開発していると思わぬ挙動を呼ぶことになるため、意識しておく必要があるでしょう。
モジュール参照
モジュールの関係としてモジュール参照があります。これはマニュアルにも明確には書いていませんが、モジュールクラスローダー間で参照関係を持つ機能です。
これは、モジュールの親子関係が発生する場合に自動的に子のモジュールクラスローダーから親のモジュールクラスローダーへの参照が付与されます。
マニュアルでは、これも親子関係のように書いてありますが、通常のクラスローダーの親子関係とは異なるため、ここではモジュール参照という別の言葉で表現します(実際にこの関係を実現するためのクラス名がModuleReferenceになっています)。
通常のクラスローダーの親子関係は、まず親に移譲して検索し、親のクラスパスに存在しなかったら子のクラスローダーが検索するという、親から子への順番でクラスを検索します。プラットフォームクラスローダーとモジュールクラスローダーの関係は、この親子関係にあたります。
モジュール参照の場合は逆に子が優先されます。まず自分自身(子モジュール)のクラスパスを検索し、参照先(親モジュール)のモジュールクラスローダーへ移譲します。mod.jsonの"includes"でロードされたモジュールのモジュールクラスローダーは、参照先(親モジュール)として扱われます。
親をランタイムモジュールとし、子をアプリケーションモジュールとするイメージと考えたら捉えやすいでしょうか。
共有データ
staticフィールドで値を共有できず、またメッセージパッシングだけでは効率が悪い場合に、異なるVerticle間でデータを共有するための方法として、SharedMapとSharedSetが用意されています。任意の名前で作成したMapやSetに共有したい値を登録することができます。
ConcurrentMap<String, Integer> map = vertx.sharedData().getMap("demo.mymap");
map.put("some-key", 123);
Set<String> set = vertx.sharedData().getSet("demo.myset");
set.add("some-value");
ただし、登録できる型にはキー、バリューとも制限があるので注意が必要です(String、Integer、Long、Boolean、Double、Float、Short、Byte、Character、byte[]、Buffer、Shareableの12種類)。基本的に不変なデータのみ共有できることになります。
不変なデータという約束はありますがShareableでラップすることで、いろいろな型も対応可能となります。
