CLOVER🍀

That was when it all began.

Quartzのクラスタリングを試してみる

これは、なにをしたくて書いたもの?

Quartzクラスタリングを扱ってみたいと思います。

Quartzクラスタリング

Quartzクラスタリングについては、あまりドキュメントがありません。

まずはこちらに少し。

Overview

それから、チュートリアルに少し書かれています。

Lesson 11: Advanced (Enterprise) Features

Quartzクラスターを構成するには、JobStoreをJDBCJobStore(JobStoreTXまたはJobStoreCMT)かTerracottaJobStoreのどちらかにする
必要があります。

機能としては、ロードバランシングとJobのフェイルオーバーが含まれるようです。

設定としては、以下がポイントのようです。

  • JDBCJobStore(JobStoreTXまたはJobStoreCMT)によるクラスタリング
    • org.quartz.jobStore.isClusteredプロパティをtrueにする
    • 一部の例外となるプロパティを除いて、同じ内容のQuartzのプロパティファイルを使用する
    • 例外は、スレッドプールサイズとorg.quartz.scheduler.instanceId
  • TerracottaJobStoreによるクラスタリング

今回は、JDBCJobStore(JobStoreTXまたはJobStoreCMT)によるクラスタリングを使用したいと思います。

注意点は、あるあるですが各Quartzノードの間で時刻同期が行われている必要があります。
ロードバランシングはランダムのようですが、実行しているJobが少ない場合はアクティブだった同じノードが優先されると書かれています。

で、クラスタリングについてのドキュメントはこれくらいだったりします。

サンプルは?と思うのですが、Exampleのページを見てもリンクがありません。

Example 13 - Clustered Quartz

  • Demonstrates how Quartz can be used in a clustered environment and how Quartz can use the database to persist scheduling information

Quartz Examples

困ったなと思いましたが、ディストリビューションにはサンプルが含まれているというので、GitHubにあるソースコードを確認してみました。

このあたりにありますね。

https://github.com/quartz-scheduler/quartz/tree/v2.3.2/distribution/examples/src/main/java/org/quartz/examples/example13

設定ファイルおよび起動スクリプトはこちら。

https://github.com/quartz-scheduler/quartz/tree/v2.3.2/distribution/src/main/assembly/root/examples/example13

設定ファイルの説明や使い方については、こちらに書かれています。

https://github.com/quartz-scheduler/quartz/blob/v2.3.2/distribution/src/main/assembly/root/examples/example13/clustering_readme.txt

サンプルプログラムの説明は、こちら。

https://github.com/quartz-scheduler/quartz/blob/v2.3.2/distribution/examples/src/main/java/org/quartz/examples/example13/ClusterExample.java#L34-L58

このあたりを参考に、Quartzクラスターを構成してみたいと思います。

スケールアウトの観点では注意点があるので、それはこちらに書きました。

Quartzのクラスタリングは、大量の小さなジョブを実行する場合はスケールしないという話 - CLOVER🍀

お題

データベースをMySQLとするJDBCJobStore(JobStoreTX)を使って、Quartzクラスターを構成します。
定期的に起動する簡単なジョブを3つのインスタンスで確認することにしましょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.4 2022-07-19
OpenJDK Runtime Environment (build 17.0.4+8-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 17.0.4+8-Ubuntu-120.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.4, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-131-generic", arch: "amd64", family: "unix"

MySQLは172.17.0.2で動作しているものとし、データベースはpractice、ユーザー/パスワードはkazuhirapasswordで作成済みとします。

$ mysql --version
mysql  Ver 8.0.31 for Linux on x86_64 (MySQL Community Server - GPL)


mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.31    |
+-----------+
1 row in set (0.06 sec)

準備

では、アプリケーションを作成する準備をしていきます。

Maven依存関係など。

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
    </dependencies>

データベースにはMySQLを使用するので、Connector/Jを依存関係に加えてあります。

SLF4Jで使用するロギングライブラリはLogbackとし、設定ファイルはこんな感じで簡単に作成。

src/main/resources/logback.xml

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

MySQL側には、QuartzのJDBCJobStoreで必要なテーブルを作成しておきます。

$ curl -sL https://raw.githubusercontent.com/quartz-scheduler/quartz/v2.3.2/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql | mysql -ukazuhira -p practice

これで、準備は完了です。

アプリケーションを作成する

それでは、ソースコードを作成します。

Example 13の構成を参考にしつつ、自分で作っていきます。

まずはJobクラス。

src/main/java/org/littlewings/quartz/PrintMessageJob.java

package org.littlewings.quartz;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrintMessageJob implements Job {
    Logger logger = LoggerFactory.getLogger(PrintMessageJob.class);

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        String instanceId = getSchedulerInstanceId(context);
        JobDetail jobDetail = context.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();

        logger.info("[{}] execute job, message = [{}]", instanceId, jobDataMap.getString("message"));
    }

    private String getSchedulerInstanceId(JobExecutionContext context) {
        try {
            return context.getScheduler().getSchedulerInstanceId();
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }
}

SchedulerインスタンスID、JobDataMapで設定したデータをログ出力する簡単なJobです。

mainクラス。

src/main/java/org/littlewings/quartz/App.java

package org.littlewings.quartz;

import java.io.Console;

import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App {
    public static void main(String... args) {
        Logger logger = LoggerFactory.getLogger(App.class);

        Scheduler scheduler = null;


        try {
            scheduler = StdSchedulerFactory.getDefaultScheduler();

            if (args.length > 0) {
                if ("register-job".equals(args[0])) {
                    JobDetail jobDetail =
                            JobBuilder
                                    .newJob(PrintMessageJob.class)
                                    .usingJobData("message", "Hello, Clustering Job!!")
                                    .withIdentity("job1", "job-group1")
                                    .build();

                    Trigger trigger =
                            TriggerBuilder
                                    .newTrigger()
                                    .withIdentity("trigger1", "trigger-group1")
                                    .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                                    .build();

                    scheduler.scheduleJob(jobDetail, trigger);
                    logger.info("registered, job");
                } else if ("clear-job".equals(args[0])) {
                    scheduler.clear();
                    logger.info("cleared, job");
                }
            }

            scheduler.start();
            logger.info("Quartz scheduler started.");

            Console console = System.console();
            console.readLine();
        } catch (SchedulerException e) {
            e.printStackTrace();
        } finally {
            if (scheduler != null) {
                try {
                    scheduler.shutdown(true);
                } catch (SchedulerException e) {
                    e.printStackTrace();
                }
            }

            AbandonedConnectionCleanupThread.checkedShutdown();
        }
    }
}

基本はSchedulerを取得して開始するだけなのですが。

            scheduler = StdSchedulerFactory.getDefaultScheduler();


            scheduler.start();
            logger.info("Quartz scheduler started.");

起動引数にregister-jobを指定するとSchedulerJobを登録し、clear-jobを指定するとSchedulerから登録したJobをクリアするようにして
います。

                if ("register-job".equals(args[0])) {
                    JobDetail jobDetail =
                            JobBuilder
                                    .newJob(PrintMessageJob.class)
                                    .usingJobData("message", "Hello, Clustering Job!!")
                                    .withIdentity("job1", "job-group1")
                                    .build();

                    Trigger trigger =
                            TriggerBuilder
                                    .newTrigger()
                                    .withIdentity("trigger1", "trigger-group1")
                                    .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                                    .build();

                    scheduler.scheduleJob(jobDetail, trigger);
                    logger.info("registered, job");
                } else if ("clear-job".equals(args[0])) {
                    scheduler.clear();
                    logger.info("cleared, job");
                }

Jobを登録するのはクラスター内のインスタンスでひとつ、というか1回だけですね。

JobCronScheduleBuilderで10秒おきに実行することにしました。

                    Trigger trigger =
                            TriggerBuilder
                                    .newTrigger()
                                    .withIdentity("trigger1", "trigger-group1")
                                    .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                                    .build();

あとは、Enterでアプリケーションが終了するようにしました。

            Console console = System.console();
            console.readLine();

これで、ソースコードの作成は完了です。

が、動作確認に移る前にQuartzの設定ファイルの作成が必要です。

設定ファイルはインスタンスごとに作成します。今回は、3つのインスタンスを実行する想定で作成しましょう。

ひとつ目のインスタンス用。

src/main/resources/instance1.properties

org.quartz.scheduler.instanceName=ClusteredExampleScheduler
org.quartz.scheduler.instanceId=instance1

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=mysqlds

org.quartz.dataSource.mysqlds.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.mysqlds.URL=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
org.quartz.dataSource.mysqlds.user=kazuhira
org.quartz.dataSource.mysqlds.password=password
org.quartz.dataSource.mysqlds.maxConnections=7

2つ目のインスタンス用。

src/main/resources/instance2.properties

org.quartz.scheduler.instanceName=ClusteredExampleScheduler
org.quartz.scheduler.instanceId=instance2

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=mysqlds

org.quartz.dataSource.mysqlds.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.mysqlds.URL=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
org.quartz.dataSource.mysqlds.user=kazuhira
org.quartz.dataSource.mysqlds.password=password
org.quartz.dataSource.mysqlds.maxConnections=7

3つ目のインスタンス用。

src/main/resources/instance3.properties

org.quartz.scheduler.instanceName=ClusteredExampleScheduler
org.quartz.scheduler.instanceId=instance3

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=mysqlds

org.quartz.dataSource.mysqlds.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.mysqlds.URL=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
org.quartz.dataSource.mysqlds.user=kazuhira
org.quartz.dataSource.mysqlds.password=password
org.quartz.dataSource.mysqlds.maxConnections=7

違いを見つけようにも間違い探しのようになっていますが、各ファイルはorg.quartz.scheduler.instanceIdが異なるだけです。

あと、ポイントはorg.quartz.jobStore.isClusteredtrueとしていることですね。

Quartzは設定ファイルを通常quartz.propertiesとして読み込みますが、他のファイルを指定する場合は-Dorg.quartz.propertiesシステムプロパティで
指定すればOKです。

By default, StdSchedulerFactory load a properties file named “quartz.properties” from the ‘current working directory’. If that fails, then the “quartz.properties” file located (as a resource) in the org/quartz package is loaded. If you wish to use a file other than these defaults, you must define the system property ‘org.quartz.properties’ to point to the file you want.

Configuration Reference

確認してみる

では、確認してみましょう。

以下のようにして実行します。ひとつ目のインスタンスのみ、register-jobを起動引数に指定しています。

## ひとつ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dexec.args=register-job \
  -Dorg.quartz.properties=instance1.properties


## 2つ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dorg.quartz.properties=instance2.properties


## 3つ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dorg.quartz.properties=instance3.properties

ひとつ目のインスタンスでの起動時のログ。

2022-10-22 19:15:06.556 [org.littlewings.quartz.App.main()] INFO  org.quartz.impl.StdSchedulerFactory - Using ConnectionProvider class 'org.quartz.utils.C3p0PoolingConnectionProvider' for data source 'mysqlds'
2022-10-22 19:15:06.604 [MLog-Init-Reporter] INFO  com.mchange.v2.log.MLog - MLog clients using slf4j logging.
2022-10-22 19:15:06.917 [org.littlewings.quartz.App.main()] INFO  com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.5.4 [built 23-March-2019 23:00:48 -0700; debug? true; trace: 10]
2022-10-22 19:15:07.088 [org.littlewings.quartz.App.main()] INFO  org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
2022-10-22 19:15:07.191 [org.littlewings.quartz.App.main()] INFO  o.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2022-10-22 19:15:07.192 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.2 created.
2022-10-22 19:15:07.194 [org.littlewings.quartz.App.main()] INFO  o.q.impl.jdbcjobstore.JobStoreTX - Using db table-based data access locking (synchronization).
2022-10-22 19:15:07.196 [org.littlewings.quartz.App.main()] INFO  o.q.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2022-10-22 19:15:07.197 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.2) 'ClusteredExampleScheduler' with instanceId 'instance1'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 5 threads.
  Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is clustered.

2022-10-22 19:15:07.197 [org.littlewings.quartz.App.main()] INFO  org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'ClusteredExampleScheduler' initialized from specified file: 'instance1.properties' in the class resource path.
2022-10-22 19:15:07.197 [org.littlewings.quartz.App.main()] INFO  org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.2
2022-10-22 19:15:07.326 [org.littlewings.quartz.App.main()] INFO  c.m.v.c.i.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> z8kfsxar1yt0gwk81b43p|586fed53, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.cj.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> z8kfsxar1yt0gwk81b43p|586fed53, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 7, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
2022-10-22 19:15:08.372 [org.littlewings.quartz.App.main()] INFO  org.littlewings.quartz.App - registered, job
2022-10-22 19:15:08.420 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler ClusteredExampleScheduler_$_instance1 started.
2022-10-22 19:15:08.420 [org.littlewings.quartz.App.main()] INFO  org.littlewings.quartz.App - Quartz scheduler started.

よーく見るとclusteredと表示されており、クラスターモードになっていることがわかります。

  Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is clustered.

Job自体は、今回はひとつ目のインスタンスで実行され続けていました。

2022-10-22 19:15:10.043 [ClusteredExampleScheduler_Worker-1] INFO  o.littlewings.quartz.PrintMessageJob - [instance1] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:15:20.035 [ClusteredExampleScheduler_Worker-2] INFO  o.littlewings.quartz.PrintMessageJob - [instance1] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:15:30.104 [ClusteredExampleScheduler_Worker-3] INFO  o.littlewings.quartz.PrintMessageJob - [instance1] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:15:40.053 [ClusteredExampleScheduler_Worker-4] INFO  o.littlewings.quartz.PrintMessageJob - [instance1] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:15:50.037 [ClusteredExampleScheduler_Worker-5] INFO  o.littlewings.quartz.PrintMessageJob - [instance1] execute job, message = [Hello, Clustering Job!!]

そこで、Enterを打ってひとつ目のインスタンスを終了してみます。

2022-10-22 19:18:26.362 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler ClusteredExampleScheduler_$_instance1 shutting down.
2022-10-22 19:18:26.362 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler ClusteredExampleScheduler_$_instance1 paused.
2022-10-22 19:18:26.667 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler ClusteredExampleScheduler_$_instance1 shutdown complete.

少し待っていると、今回は3つ目のインスタンスが検知したようです。

2022-10-22 19:18:39.266 [QuartzScheduler_ClusteredExampleScheduler-instance3_ClusterManager] INFO  o.q.impl.jdbcjobstore.JobStoreTX - ClusterManager: detected 1 failed or restarted instances.
2022-10-22 19:18:39.266 [QuartzScheduler_ClusteredExampleScheduler-instance3_ClusterManager] INFO  o.q.impl.jdbcjobstore.JobStoreTX - ClusterManager: Scanning for instance "instance1"'s failed in-progress jobs.
2022-10-22 19:18:39.383 [ClusteredExampleScheduler_Worker-1] INFO  o.littlewings.quartz.PrintMessageJob - [instance3] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:18:40.135 [ClusteredExampleScheduler_Worker-2] INFO  o.littlewings.quartz.PrintMessageJob - [instance3] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:18:50.062 [ClusteredExampleScheduler_Worker-3] INFO  o.littlewings.quartz.PrintMessageJob - [instance3] execute job, message = [Hello, Clustering Job!!]

この後、Jobは3つ目のインスタンスで実行され続けました。

停止したひとつ目のインスタンスは、起動引数を指定しなければ再度クラスターに参加できます。

$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dorg.quartz.properties=instance1.properties

この後、インスタンスを停止したり、再度起動したりするとアクティブなインスタンスを移りつつもJobは実行され続けることが確認できます。

今回は実行するJobが少ないからか、ロードバランシグ的な動作は確認できませんでしたが…。

ここで、すべてのインスタンスを1度終了して、登録したJobをクリアしておきます。

$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dexec.args=clear-job \
  -Dorg.quartz.properties=instance1.properties

このインスタンス自体も終了させる必要がありますが…。

インスタンスのIDを自動採番する

今回、各インスタンスのIDを個々に指定するためにそれぞれ設定ファイルを作成しました。

とはいえ、インスタンスの数が増減したりすると、インスタンスごとに設定ファイルを用意するのは面倒かもしれません。

ここで、org.quartz.scheduler.instanceIdプロパティの説明を見るとAUTOと指定することでインスタンスのIDを自動採番できそうなことが
わかりました。

org.quartz.scheduler.instanceId

You may use the value “AUTO” as the instanceId if you wish the Id to be generated for you.

Configuration Reference

こちらを試してみましょう。

設定ファイルをquartz.propertiesにして、org.quartz.scheduler.instanceIdAUTOと指定します。

src/main/resources/quartz.properties

org.quartz.scheduler.instanceName=ClusteredExampleScheduler
org.quartz.scheduler.instanceId=AUTO

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=mysqlds

org.quartz.dataSource.mysqlds.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.mysqlds.URL=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
org.quartz.dataSource.mysqlds.user=kazuhira
org.quartz.dataSource.mysqlds.password=password
org.quartz.dataSource.mysqlds.maxConnections=7

今度は、-Dorg.quartz.propertiesシステムプロパティでQuartzの設定ファイルを指定せずに起動します。ひとつ目のインスタンスには起動引数を
指定するのは、先ほどと同じです。

## ひとつ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App \
  -Dexec.args=register-job


## 2つ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App


## 3つ目のインスタンス
$ mvn compile exec:java \
  -Dexec.mainClass=org.littlewings.quartz.App

起動時のインスタンスIDを見ると、自動採番されているようなのですが、どうもホスト名が入っているようです。

2022-10-22 19:32:18.952 [org.littlewings.quartz.App.main()] INFO  org.quartz.core.QuartzScheduler - Scheduler ClusteredExampleScheduler_$_myhost1666434737522 started.
2022-10-22 19:32:18.954 [org.littlewings.quartz.App.main()] INFO  org.littlewings.quartz.App - Quartz scheduler started.

Jobの実行ログ。

2022-10-22 19:32:20.044 [ClusteredExampleScheduler_Worker-1] INFO  o.littlewings.quartz.PrintMessageJob - [myhost1666434737522] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:32:30.145 [ClusteredExampleScheduler_Worker-2] INFO  o.littlewings.quartz.PrintMessageJob - [myhost1666434737522] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:32:40.492 [ClusteredExampleScheduler_Worker-3] INFO  o.littlewings.quartz.PrintMessageJob - [myhost1666434737522] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:32:50.051 [ClusteredExampleScheduler_Worker-4] INFO  o.littlewings.quartz.PrintMessageJob - [myhost1666434737522] execute job, message = [Hello, Clustering Job!!]
2022-10-22 19:33:00.033 [ClusteredExampleScheduler_Worker-5] INFO  o.littlewings.quartz.PrintMessageJob - [myhost1666434737522] execute job, message = [Hello, Clustering Job!!]

やっぱりホスト名が入っています。また、ホスト名の後ろに付いているのはタイムスタンプな感じがしますね。

ちょっとドキュメントを見てみましょう。インスタンスIDの採番については、org.quartz.scheduler.instanceIdGenerator.classプロパティで
クラスを指定できるみたいです。

org.quartz.scheduler.instanceIdGenerator.class

Only used if org.quartz.scheduler.instanceId is set to “AUTO”. Defaults to “org.quartz.simpl.SimpleInstanceIdGenerator”, which generates an instance id based upon host name and time stamp. Other IntanceIdGenerator implementations include SystemPropertyInstanceIdGenerator (which gets the instance id from the system property “org.quartz.scheduler.instanceId”, and HostnameInstanceIdGenerator which uses the local host name (InetAddress.getLocalHost().getHostName()). You can also implement the InstanceIdGenerator interface your self.

Configuration Reference

ここでorg.quartz.scheduler.instanceIdAUTOを指定した場合はorg.quartz.simpl.SimpleInstanceIdGeneratorが使われる、となっています。

InstanceIdGeneratorが、インスタンスIDを採番するインターフェースです。

InstanceIdGenerator (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)

SimpleInstanceIdGeneratorは、ホスト名と現在時刻でインスタンスIDを作成するInstanceIdGeneratorの実装です。

The default InstanceIdGenerator used by Quartz when instance id is to be automatically generated. Instance id is of the form HOSTNAME + CURRENT_TIME.

SimpleInstanceIdGenerator (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)

その他、ホスト名をインスタンスIDとするものや、システムプロパティでインスタンスIDを指定できる実装もあるようです。

HostnameInstanceIdGenerator (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)

SystemPropertyInstanceIdGenerator (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)

他の採番ルールにしたかったら、自分でInstanceIdGeneratorインターフェースの実装を作成して
org.quartz.scheduler.instanceIdGenerator.classプロパティで指定すれば良さそうですね。

今回は、こんなところでしょうか。

まとめ

Quartzクラスタリングを試してみました。

とても簡単に使えたので良いのですが、今回はロードバランシング的なところが確認できなかったのがちょっと残念ですね。

まあ、基本的なところは確認できたと思うので、今回はこれで良いでしょう。

WildFly Bootable JARで作成したアプリケーションの起動時に、システムプロパティや起動引数を指定する

これは、なにをしたくて書いたもの?

これまで何回かWildFly Bootable JARを使ってきましたが、そういえばWildFlyで使っていたようなシステムプロパティや起動引数の指定は
有効なのかな?とふと疑問に思ったので試してみました。

結論を言うと、同じように使えます。

WildFly Bootable JARの起動引数について

WildFly Bootable JARの起動引数については、こちらに書かれています。

Bootable JAR Guide / WildFly bootable JAR application development / Running your application / Bootable JAR arguments

以下のように、まあ見慣れたものがありますね。

  • -b[interface]=<value> … Set system property jboss.bind.address. to the given value
  • -b=<value> … Set system property jboss.bind.address to the given value
  • -D<name>[=<value>] … Set a system property. The system properties are set by the server. They are not set by the bootable JAR JVM.
  • --help … Display help then exit …
  • -u=<value> … Set system property jboss.default.multicast.address to the given value.
  • --version … Print version and exit.

--helpも使えるんですね。

ここで、-Dを使ったシステムプロパティで指定できるのは、どのあたりなんだろうと思ったのが今回扱ったきっかけです。

お題

今回は、WildFly Bootable JARを使ってひとつのホスト内に同じアプリケーションを2つ起動させてみます。この時、なにも考えずに起動すると
ポートが重複するはずですが、-Djboss.socket.binding.port-offsetでポートをずらせるかどうか確認したいと思います。

せっかくなので、クラスタリングもしておきましょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.4 2022-07-19
OpenJDK Runtime Environment (build 17.0.4+8-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 17.0.4+8-Ubuntu-120.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.4, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-128-generic", arch: "amd64", family: "unix"

WildFly Bootable JARを使ったサンプルアプリケーションを作成する

それでは、アプリケーションを作成します。

まずはpom.xmlから。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>wildfly-bootable-jar-clustering</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>8.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-jar-maven-plugin</artifactId>
                <version>8.0.1.Final</version>
                <configuration>
                    <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)#26.1.2.Final</feature-pack-location>
                    <layers>
                        <layer>jaxrs</layer>
                        <layer>web-clustering</layer>
                    </layers>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

レイヤーには、jaxrsweb-clusteringを入れました。

                    <layers>
                        <layer>jaxrs</layer>
                        <layer>web-clustering</layer>
                    </layers>

使用するWildFlyは26.1.2.Finalとします。

HttpSessionを使ったJAX-RSリソースクラス。

src/main/java/org/littlewings/wildfly/bootable/HelloResource.java

package org.littlewings.wildfly.bootable;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("hello")
public class HelloResource {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String messageAndSessionStartedTime(@Context HttpServletRequest request) {
        HttpSession session = request.getSession();

        LocalDateTime startedTime;

        if (session.isNew()) {
            startedTime = LocalDateTime.now();
            session.setAttribute("startedTime", startedTime);
        }

        return String.format(
                "Hello[%s]",
                ((LocalDateTime) session.getAttribute("startedTime")).format(DateTimeFormatter.ISO_DATE_TIME)
        );
    }
}

JAX-RSの有効化。

src/main/java/org/littlewings/wildfly/bootable/JaxrsActivator.java

package org.littlewings.wildfly.bootable;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("")
public class JaxrsActivator extends Application {
}

<distributable/>が必要なので、web.xmlも作成。

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <distributable/>
</web-app>

パッケージング。

$ mvn package

起動。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

動作確認。

$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]

ひとまず、シングルノードのアプリケーションとしてはOKです。

2つ目のアプリケーションを起動する

では、この状態で同じアプリケーションをもうひとつ起動します。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

当然のことながら、起動に失敗します。

00:05:55,841 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-7) MSC000001: Failed to start service org.wildfly.undertow.listener.default: org.jboss.msc.service.StartException in service org.wildfly.undertow.listener.default: アドレスは既に使用中です /127.0.0.1:8080
        at org.wildfly.extension.undertow@26.1.2.Final//org.wildfly.extension.undertow.ListenerService.start(ListenerService.java:212)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:833)

00:05:56,786 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 30) MSC000001: Failed to start service org.wildfly.clustering.jgroups.channel.ee: org.jboss.msc.service.StartException in service org.wildfly.clustering.jgroups.channel.ee: java.lang.IllegalStateException: java.lang.Exception: failed to open a port in range 55200-55200 (last exception: java.net.BindException: アドレスは既に使用中です)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.FunctionalService.start(FunctionalService.java:66)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.AsyncServiceConfigurator$AsyncService.lambda$start$0(AsyncServiceConfigurator.java:117)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:833)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: java.lang.IllegalStateException: java.lang.Exception: failed to open a port in range 55200-55200 (last exception: java.net.BindException: アドレスは既に使用中です)
        at org.jboss.as.clustering.jgroups@26.1.2.Final//org.jboss.as.clustering.jgroups.subsystem.ChannelServiceConfigurator.get(ChannelServiceConfigurator.java:116)
        at org.jboss.as.clustering.jgroups@26.1.2.Final//org.jboss.as.clustering.jgroups.subsystem.ChannelServiceConfigurator.get(ChannelServiceConfigurator.java:58)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.FunctionalService.start(FunctionalService.java:63)
        ... 7 more

ここで、-Djboss.socket.binding.port-offsetを指定して起動しなおします。

$ java -Djboss.socket.binding.port-offset=1000 -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

今度は起動に成功するので、動作確認してみましょう。

$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:9080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:9080/hello
Hello[2022-10-19T00:04:39.439503952]

OKですね。クラスタリングもうまくいってみます。

これで、今回のお題の確認は終わりました。

ヘルプを見てみる

ヘルプを見てみます。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar --help

ふつうに表示されて、ちょっと面白いです。

Usage: java -jar <bootable jar> [args...]
where args include:
    --deployment=<value>                Path to deployment artifact
                                        (war,jar,ear or exploded deployment
                                        dir) to deploy in hollow jar


    -b=<value>                          Set system property jboss.bind.address
                                        to the given value


    -b<interface>=<value>               Set system property
                                        jboss.bind.address.<interface> to the
                                        given value


    -D<name>[=<value>]                  Set a system property


    --cli-script=<value>                Path to a CLI script to execute when
                                        starting the Bootable JAR


    --display-galleon-config            Display the content of the Galleon
                                        configuration used to build this
                                        bootable JAR


    --help                              Display this message and exit


    --install-dir=<value>               Path to directory in which the server
                                        is installed. By default the server is
                                        installed in TEMP directory.


    --properties=<url>                  Load system properties from the given
                                        url


    -secmgr                             Activate the SecurityManager


    -S<name>[=<value>]                  Set a security property


    -u=<value>                          Set system property
                                        jboss.default.multicast.address to the
                                        given value


    --version                           Print version and exit

java -jarで実行するので-Dで指定するシステムプロパティは-jarよりも前の指定になりますが

$ java -Djboss.http.port=9080 -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

その他の引数を指定する場合は、コマンドライン引数として指定することになります。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar -b=192.168.0.6

それはそうと、--versionでバージョンが見られるのが個人的にはちょっと意表を突かれた感じがしておもしろかった(?)です。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar --version
WildFly Full 26.1.2.Final (WildFly Core 18.1.2.Final)

どうなっている?

ところで、-Djboss.socket.binding.port-offsetなどがふつうに使えましたが、どうなっているんでしょう?

standalone.xmlでも入っているのかなと思い、Bootable JARからXMLファイルを探してみます。

$ unzip -l target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar | grep xml

standalone.xmlファイルは入っていないのですが、provisioning.xmlというファイルがあったのでこちらを見てみます。

$ unzip -p target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar provisioning.xml
<?xml version="1.0" ?>

<installation xmlns="urn:jboss:galleon:provisioning:3.0">
    <feature-pack location="wildfly@maven(org.jboss.universe:community-universe)#26.1.2.Final">
        <default-configs inherit="false"/>
        <packages inherit="false"/>
    </feature-pack>
    <config model="standalone" name="standalone.xml">
        <layers>
            <include name="jaxrs"/>
            <include name="web-clustering"/>
        </layers>
    </config>
    <options>
        <option name="optional-packages" value="passive+"/>
    </options>
</installation>

WildFly Bootable JARを作成するためにWildFly JAR Maven Pluginに指定した内容が、ほぼ入っている感じですね。

で、stanalone.xmlから指定したレイヤーだけ使っている感じです。

    <config model="standalone" name="standalone.xml">
        <layers>
            <include name="jaxrs"/>
            <include name="web-clustering"/>
        </layers>
    </config>

そして、作成されたJARファイルの中を見ていると、wildfly.zipというひときわ大きなファイルがあります。

$ unzip -l target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar | grep wildfly.zip
 90700689  2022-10-19 00:04   wildfly.zip

ちなみに、今回作成したJARファイルは87Mでした。

$ ll -h target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar
-rw-rw-r-- 1 xxxxx xxxxx 87M 10月 19 00:04 target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

wildfly.zipを取り出してみます。

$ unzip target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar wildfly.zip
Archive:  target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar
  inflating: wildfly.zip             

今回のBootable JARは、ほぼwildfly.zipのサイズですね。

$ ll -h wildfly.zip
-rw-rw-r-- 1 xxxxx xxxxx 87M 10月 19 00:04 wildfly.zip

中身は、サイズが小さくなったWildFlyみたいですね。WildFly JAR Maven Pluginで指定したレイヤーが必要とする程度のサイズになっている
ような気がします。

standalone.xmlを取り出してみます。

$ unzip wildfly.zip standalone/configuration/standalone.xml
Archive:  wildfly.zip
  inflating: standalone/configuration/standalone.xml

standalone.xmlの最後の方を見ると、今回作成したWARファイルがデプロイされる設定になっています。

    <deployments>
        <deployment name="wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT.war" runtime-name="ROOT.war">
            <content sha1="6c47a2feb9d4b149c7b26dfbfb7e172646d6e6ed"/>
        </deployment>
    </deployments>

ということは、こちらがWildFly Bootable JARで起動されるWildFlyの実体なんでしょうね。

standalone.xml自体は通常のWildFlyのものとほとんど変わらないようですね。
以下はwildfly.zipに含まれていたstandalone.xmlsocket-binding-groupの抜粋です。

    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
        <socket-binding name="http" port="${jboss.http.port:8080}"/>
        <socket-binding name="https" port="${jboss.https.port:8443}"/>
        <socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
        <socket-binding name="jgroups-tcp" interface="private" port="7600"/>
        <socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
        <socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
        <socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
        <socket-binding name="txn-recovery-environment" port="4712"/>
        <socket-binding name="txn-status-manager" port="4713"/>
    </socket-binding-group>

standalone.xml内で、レイヤーで使えるようにしている範囲は起動時にシステムプロパティで変更できそうです。今回もそれを利用しましたし。

なんとなく雰囲気がわかったので、今回はこのあたりでOKかなと思います。

構成をもっと大きく変えたい場合は?

システムプロパティや起動引数の範囲ではなく、そもそもサブシステムの設定等を変えたい場合はこちらを参照、ですね。

管理CLIスクリプトを使って変更できたりします。

まとめ

今回は、WildFly Bootable JARで作成したアプリケーションの起動時に、システムプロパティや起動引数が指定できたりするのか?WildFly
どう違うのか?という点でちょっと見てみました。

割とそのままでしたね。

ちょっと気にしすぎた感じもしますが、これで確認できたので今後は安心して使えそうです。