これは、なにをしたくて書いたもの?
Quartzのクラスタリング
Quartzのクラスタリングについては、あまりドキュメントがありません。
まずはこちらに少し。
それから、チュートリアルに少し書かれています。
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
によるクラスタリングTerracottaJobStore
を使用して複数インスタンスで構成するだけでよい
今回は、JDBCJobStore(JobStoreTX
またはJobStoreCMT
)によるクラスタリングを使用したいと思います。
注意点は、あるあるですが各Quartzノードの間で時刻同期が行われている必要があります。
ロードバランシングはランダムのようですが、実行しているJobが少ない場合はアクティブだった同じノードが優先されると書かれています。
で、クラスタリングについてのドキュメントはこれくらいだったりします。
サンプルは?と思うのですが、Exampleのページを見てもリンクがありません。
Example 13 - Clustered Quartz
困ったなと思いましたが、ディストリビューションにはサンプルが含まれているというので、GitHubにあるソースコードを確認してみました。
このあたりにありますね。
設定ファイルおよび起動スクリプトはこちら。
設定ファイルの説明や使い方については、こちらに書かれています。
サンプルプログラムの説明は、こちら。
このあたりを参考に、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
、ユーザー/パスワードはkazuhira
/password
で作成済みとします。
$ 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の構成を参考にしつつ、自分で作っていきます。
- 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
まずは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
を指定するとScheduler
にJob
を登録し、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回だけですね。
Job
はCronScheduleBuilder
で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.isClustered
をtrue
としていることですね。
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.
確認してみる
では、確認してみましょう。
以下のようにして実行します。ひとつ目のインスタンスのみ、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.
こちらを試してみましょう。
設定ファイルをquartz.properties
にして、org.quartz.scheduler.instanceId
をAUTO
と指定します。
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.
ここでorg.quartz.scheduler.instanceId
にAUTO
を指定した場合は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
プロパティで指定すれば良さそうですね。
今回は、こんなところでしょうか。
まとめ
とても簡単に使えたので良いのですが、今回はロードバランシング的なところが確認できなかったのがちょっと残念ですね。
まあ、基本的なところは確認できたと思うので、今回はこれで良いでしょう。