Payara Micro の設計と実装 - 2017 年版

この記事は Java Advent Calendar 2017 および Java EE Advent Calendar 2017 の 10 日目です。昨日分は @skht777 さんの「Javaにおけるインタフェースの使い道を今一度考える」(Java Advent Calendar 2017) と @TTakakiyo さん (Java EE Advent Calendar 2017) です。

本日は、サイズ 70 MB に満たない小さな Java EE ランタイムである Payara Micro について、深く探ってゆきたいと思います。

Payara Micro とは?

Payara Micro は、Payara Server から派生した埋め込み用途向け実装です。Payara Server は Java EE のリファレンス実装である GlassFish Server Open Source Edition をベースに商用システム向けの機能と有償サポートを追加したものです (有償サポートは Payara Serviced Limited が提供しています)。Payara Server と Payara Micro は Payara Foundation が有するオープンソース・ソフトウェアであり、コミュニティの開発者らによって活発に開発が行われています。年 4 回 (通例では 2 月、5 月、8 月、11 月) の定期的なリリースが行われ、先進的な機能と高い信頼性の両立を図っています。Payara Micro は以下の特徴を持ちます。

  • フットプリントが小さくマイクロサービスに最適
  • Java EE の標準 API (Web Profile + α) に加え Eclipse MicroProfile にも準拠
  • Hazelcast による自動クラスタリングと JCache サポート
  • 商用品質 (有償サポートもあり)

本稿執筆時点での最新版は、Payara Micro / Payara Server ともに 4.1.2.174 です。Java EE 8 に対応した Payara Micro 5 / Payara Server 5 のプレビュー版も公開されており、Java EE 8 の新機能をいち早く試すこともできます。

Payara Micro ひとめぐり

Payara Micro はサイズ 70 MB に満たない JAR ファイル 1 つだけで構成されます。本稿では便宜上、Payara Micro の JAR ファイルを payara-micro.jar とします (実際にダウンロードしたものには payara-micro-4.1.2.174.jar のようにバージョン番号が付きます)。Payara Micro の起動はとても簡単で、コマンド 1 つでできます。

java -jar payara-micro.jar

終了は Ctrl + C です。

これだけでは何もできないため、アプリケーションをデプロイして起動します。--deploy filename オプションを付けると、filename がデプロイされた状態で起動します。標準のポートは 8080 (HTTP) です。--port number オプションで変更できますし、Auto Binding 機能を使うと開いているポートを自動的にスキャンすることも可能です。

java -jar payara-micro.jar --deploy app.war

Payara Micro は他の類似ランタイムと異なり、複数のアプリケーションをデプロイすることができます。

java -jar payara-micro.jar --deploy app1.war --deploy app2.war

あるディレクトリ内のすべてのアプリケーションをデプロイする、というオペレーションも可能です。

java -jar payara-micro.jar --deploydir appsdir

デプロイできるのは WAR ファイルだけではありません。RAR ファイル (リソース・アダプタ) も対象であり、JCA 経由で外部システム (クラウドのメッセージング・サービス等) にアクセスするアプリケーションも実行可能です。以下は Azure Service Bus に接続するアプリケーションをデプロイする例です。

java -jar payara-micro.jar --deploy app.war --deploy azure-sb-rar-0.2.0.rar

なお、Payara Micro には Maven リポジトリ上の WAR ファイルや RAR ファイルを直接デプロイする機能も備わっています。Maven Central 上のものであればリポジトリの URL を明示する必要もありません。

java -jar payara-micro.jar --deploy app.war --deployfromgav fish.payara.cloud.connectors.azuresb:azure-sb-rar:0.2.0

他の類似ランタイムと同じように、Payara Micro も Uber JAR (Fat JAR) で実行することができます。最初に Uber JAR を作成する必要があります。通常起動時のオプションをすべて付けた状態で --outputuberjar filename オプションを追加してください。

java -jar payara-micro.jar --deploy app.war --outputuberjar app.jar

java -jar app.jar

--outputuberjar オプションを付けて実行した時にはアプリケーションは実行されず、Uber JAR の作成のみを行います。付加したオプションはすべて Uber JAR のデフォルトとして引き継がれます。Uber JAR の実行時にオプションを追加することも可能です。

Payara Micro は同一ネットワーク内で 2 インスタンス以上起動すると、Hazelcast の機能により自動的にクラスタが構成されます。最初のインスタンスを起動した時には以下のようなログがコンソールに出力されているはずです。

Members [1] {
        Member [192.168.184.68]:5900 - 963637b0-0b97-4307-af44-1c7ba3ccb4d4 this
}

2 つ目のインスタンスを起動すると、さらに以下のようなログがコンソールに出力され、2 インスタンス間でのクラスタリングが行われていることが確認できます。

Members [2] {
        Member [192.168.184.68]:5900 - 963637b0-0b97-4307-af44-1c7ba3ccb4d4 this
        Member [192.168.184.67]:5900 - eec56b79-b81c-4dc2-b330-b46076f65c03
}

なお、この自動クラスタリングは Payara Micro 同士だけでなく、Hazelcast を有効にした Payara Server との間でも行われます。Payara Micro 同士でのセッション・レプリケーションについては、2015 年の Java EE Advent Calendar の 9 日目、@koduki さんによる「さよならスティッキーセッション!PayaraでJavaEEでもセッションをKVSに。」に詳細があります。2 年前の記事ですが最新の Payara Micro でも有効です。Hazelcast による自動クラスタリングは Payara Micro が誕生時から持っている優れた機能で、これを知ってしまうと他のサーバーのクラスタ構築をやる気が失せてしまうほどです。

Payara Micro のドキュメント (英語) には、さらに高度なトピックが掲載されています。Payara Server が持つ商用システム向け機能もそのまま Payara Micro に入っています (有効化する手順が異なるだけです)。

Payara Micro についてイメージしていただけたでしょうか? ミドルウェアですので、概要紹介だけとなるとそれほどの量ではありません。

Payara Micro の設計と実装

Payara Micro の設計と実装については、実は昨年の Payara Advent Calendar で紹介しています。さらにその直前に開催された JJUG CCC 2016 Fall でプレゼンテーションも行っています。

当時のバージョンは 4.1.1.164 だったのですが、その次の 4.1.1.171 から Payara Micro のアーキテクチャが大幅に変わってしまい、発表からわずか 2 ヶ月で過去のものになりました。とは言っても、その背景にあるものは変わっていないため、感覚だけ掴んでいただければ十分です。以下、昨年の記事からの引用です。

50 分という時間制限の中では Payara Micro のすべてを解説することはできないため、ブートストラップとシャットダウン、Hazelcast による自動クラスタリング、アプリケーションのデプロイ、Uber Jar の生成に内容を絞りました。いずれも Payara Micro 独自の機能であり、深く解説するには好都合なトピックでした。また、いくつかの機能については多くのアプリケーション・サーバーで共通しているものであり、アプリケーション・サーバー一般論を理解する上でも何からのヒントになったのではないかと思っています。

今回、一番伝えたかったことは、多くの Java アプリケーション開発者にとってブラックボックスとなっている Java EE アプリケーション・サーバーであっても、結局は main メソッドを持つ Java アプリケーションに過ぎないということです。Payara Micro だけでなく Tomcat、WildFly、GlassFish のブートストラップに関する情報をスライドに載せたのも、そのことを意図しています。このことさえ分かれば、Java EE アプリケーション・サーバーはもはやブラックボックスとは思えなくなります。

Payara Micro 4.1.1.171 で大きく変わったのは、全体を Nested JAR で再構築したことです。Payara Micro 4.1.1.164 までは Payara Server の JAR を一旦展開してから Payara Micro の JAR にまとめ直していたのですが、Payara Micro 4.1.1.171 からは Payara Server の JAR をそのまま取り込むように変更されています。その関係でブートストラップが大幅に書き換えられ、入れ子になった JAR からクラスをロードできるように変更されています。正確には、fish.payara.micro.PayaraMicro クラスの main メソッドが呼び出された後、モジュールのロードを行う fish.payara.micro.boot.PayaraMicroLauncher が呼び出されます。そこから先を追うのはかなり大変ですが、結論だけを述べると、Payara Micro 本体とデプロイされたアプリケーションが一時ディレクトリに展開され (この時点で JAR の入れ子は解消される)、その後は一般的な Java EE アプリケーション・サーバーのように振る舞います。実は展開先のディレクトリはオプション --unpackdir directory を用いると明示的に指定可能で、Payara Micro が内部で何をやっているのかを垣間見ることができます。

java -jar payara-micro.jar --deploy app.war --unpackdir unpack

展開先のディレクトリは以下のようになっています。

unpackdir.png

  • applications - デプロイしたアプリケーションが格納されています。
  • autodeploy - 自動デプロイのディレクトリ、事実上未使用です。
  • config - domain.xml 等の構成情報が含まれていますが、Payara Server よりも限定されています。
  • docroot - ドキュメント・ルート・ディレクトリ、事実上未使用です。
  • generated - 内部的に自動生成されたものが格納されます。
  • lib - アプリケーションとは別個に追加されたライブラリ、全アプリケーション共通で使用するものです。
  • logs - ログ・ディレクトリ。server.log はありますが、基本的には使用されません。
  • META-INF - メタ情報。META-INF が入っているだけです。
  • runtime - Payara Server/Micro の主要なモジュールが含まれます。
  • .rar - JDBC コネクション・プール関連のリソース・アダプタ

Payara Server を普段ご利用の方であればお分かりになると思いますが、Payara のドメインとディレクトリ構成がよく似ています。結局のところ、これらを asadmin で動的に制御できるのが Payara Server、起動時に固定してしまうのが Payara Micro です。アプリケーション・サーバーと言えども、結局のところは Java のプログラムに過ぎないのです。

Payara Micro 4.1.1.171 以降の起動時、最初に一瞬沈黙してからログを出力するのは、その沈黙の間に一時ディレクトリへこれらのファイルを展開しているからです。

実は、一時ディレクトリへ展開せずに Payara Micro を動作させることもできます。Nested JAR をインメモリで展開して処理する方法で、オプション --nested を使用します。

java -jar payara-micro.jar --deploy app.war --nested

ほとんどの環境では、--nested オプションを使うと全体的に処理が遅くなります。私の環境でも起動時間が 10% ほど長くなりました。ストレージが非常に遅く一時ディレクトリへの展開に時間がかかるようであれば、--nested オプションを試す価値はあります (必ずオプション有無両方を試して、良好な方を選択してください)。

Payara Micro と JDK 9

Payara Micro 4.1 系は JDK 8 では動作しますが JDK 9 では動作しません。とは言うものの、Payara Micro 本体だけであれば、以下のような Jigsaw の価値をぶっ飛ばす暴力的なやり方で起動させることはできます。

java --add-modules=ALL-SYSTEM payara-micro-4.1.2.174.jar

ただし、アプリケーションをデプロイしようとすると、この方法を用いても sun.misc.URLClassPath クラスが見つからないためエラー (例外ではなく) になります。これに関しては十中八九 JDK 9 側のクラスローダー周りの仕様変更が影響していると思われますが、ほかにも不具合が出てくることは想像に難くありません。

これは Payara Services Limited の公式見解ですが、Payara Micro および Payara Server は現行の JDK 9 には対応させません。Payara Micro は最長 10 年間の Long Term Support (LTS) ですが、現行の JDK 9 は LTS ではないため対応させる意味がないという判断によります。ただし、開発中の Payara Micro 5 については、次の LTS 版 JDK がリリースされ次第対応する予定となっています。この件については GitHub Issues で何度も言及されています

この件があって sun.misc.* 系クラスの使用状況を調べてみたのですが、Payara Micro では直接参照している個所は存在せず、Payara Server や GlassFish でも使用箇所はほとんどまたは全くないと考えられます。個人的には、これほど後方互換性を損なってまで Jigsaw を導入し標準 API を再構成する価値があったのか甚だ疑問です。