The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

スケジューラーリファレンスガイド

最近のアプリケーションでは、定期的に特定のタスクを実行する必要があります。Quarkusには2つのスケジューラーエクステンションがあります。 quarkus-scheduler エクステンションには、APIと軽量なインメモリースケジューラーの実装が含まれています。 quarkus-quartz エクステンションは、 quarkus-scheduler エクステンションのAPIを実装し、Quartzライブラリをベースにしたスケジューラーの実装が含まれています。 quarkus-quartz が必要になるのは、永続的タスク、クラスターリング、ジョブのプログラマティックスケジューリングなど、より高度なスケジューリングのユースケースのみです。

プロジェクトに quarkus-quartz 依存関係を追加すると、 quarkus-scheduler エクステンションからの軽量スケジューラーの実装は自動的に無効になります。

1. スケジュールされたメソッド

A method annotated with @io.quarkus.scheduler.Scheduled is automatically scheduled for invocation. A scheduled method must not be abstract or private. It may be either static or non-static. A scheduled method can be annotated with interceptor bindings, such as @javax.transaction.Transactional and @org.eclipse.microprofile.metrics.annotation.Counted.

If there is a bean class that has no scope and declares at least one non-static method annotated with @Scheduled then @Singleton is used.

さらに、アノテーションされたメソッドは void を返し、パラメーターを宣言しないか、 io.quarkus.scheduler.ScheduledExecution 型のパラメーターを 1 つだけ宣言する必要があります。

アノテーションは繰り返し可能なので、1つのメソッドを複数回スケジュールすることができます。

Subclasses never inherit the metadata of a @Scheduled method declared on a superclass. In the following example, the everySecond() method is only invoked upon the instance of Jobs.

class Jobs {

   @Scheduled(every = "1s")
   void everySecond() {
     // ..do something
   }
}

@Singleton
class MyJobs extends Jobs {
}

A CDI event of type io.quarkus.scheduler.SuccessfulExecution is fired synchronously and asynchronously when an execution of a scheduled method is successful. A CDI event of type io.quarkus.scheduler.FailedExecution is fired synchronously and asynchronously when an execution of a scheduled method throws an exception.

1.1. トリガー

A trigger is defined either by the @Scheduled#cron() or by the @Scheduled#every() attribute. If both are specified, the cron expression takes precedence. If none is specified, the build fails with an IllegalStateException.

1.1.1. CRON

CRONトリガーは、cronライクな式で定義されています。例えば "0 15 10 * * ?" は毎日午前10時15分に起動します。

CRONトリガーの例
@Scheduled(cron = "0 15 10 * * ?")
void fireAt10AmEveryDay() { }

CRON 式で使用される構文は quarkus.scheduler.cron-type プロパティーによって制御されます。 値は cron4j , quartz , unix および spring のいづれかです。quartz がデフォルトで使用されます。

cron 属性は、デフォルト値やネストされたプロパティ式を含む プロパティ式 をサポートしています。(なお、"{property.path}"スタイルの表現もサポートされていますが、プロパティ式の完全な機能は提供されていません)。

CRON設定プロパティーの例
@Scheduled(cron = "${myMethod.cron.expr}")
void myMethod() { }

特定のスケジュールメソッドを無効にしたい場合は、そのcron式を "off" または "disabled" に設定します。

application.properties
myMethod.cron.expr=disabled

プロパティ式では、そのプロパティが構成されていない場合に使用されるデフォルト値を定義することができます。

デフォルト値が 0 0 15 ? * MON * のCRON設定プロパティーの例
@Scheduled(cron = "${myMethod.cron.expr:0 0 15 ? * MON *}")
void myMethod() { }

プロパティ myMethod.cron.expr が未定義または null の場合は、デフォルト値( 0 0 15 ? * MON * )が使用されます。

1.1.2. インターバル

インターバルトリガーは、呼び出しの間の期間を定義します。期間式は ISO-8601 の期間フォーマット PnDTnHnMn.nS に基づいており、 @Scheduled#every() の値は java.time.Duration#parse(CharSequence) で解析されます。ただし、式が数字で始まる場合は、 PT の接頭辞が自動的に追加されます。例えば、 PT15M の代わりに 15m を使用することができ、「15 分」と解析されます。

インターバルトリガーの例
@Scheduled(every = "15m")
void every15Mins() { }

every 属性は、デフォルト値やネストされたプロパティ式を含む プロパティ式 をサポートしています。(なお、 "{property.path}" スタイル式はサポートされていますが、プロパティ式の完全な機能は提供されていません)。

インターバル設定プロパティーの例
@Scheduled(every = "${myMethod.every.expr}")
void myMethod() { }

Intervals は、その値を "off" または "disabled" に設定することで無効にすることができます。そのため、例えば、デフォルト値 "off" のプロパティ式は、そのConfig Propertyが設定されていない場合、トリガーを無効にするために使用することができます。

デフォルト値のあるインターバル設定プロパティーの例
@Scheduled(every = "${myMethod.every.expr:off}")
void myMethod() { }

1.2. アイデンティティ

By default, a unique identifier is generated for each scheduled method. This identifier is used in log messages, during debugging and as a parameter of some io.quarkus.scheduler.Scheduler methods. Therefore, a possibility to specify an explicit identifier may come in handy.

アイデンティティの例
@Scheduled(identity = "myScheduledMethod")
void myMethod() { }

identity 属性は、デフォルト値やネストされたプロパティ式を含む プロパティ式をサポートしています。(なお、 "{property.path}" スタイル式はサポートされていますが、プロパティ式の完全な機能は提供されていません)。

インターバル設定プロパティーの例
@Scheduled(identity = "${myMethod.identity.expr}")
void myMethod() { }

1.3. 遅延実行

@Scheduled では、トリガーが発報を開始時刻を遅らせるための2つの方法を提供しています。

@Scheduled#delay()@Scheduled#delayUnit() は初期遅延を一緒に形成します。

@Scheduled(every = "2s", delay = 2, delayUnit = TimeUnit.HOUR) (1)
void everyTwoSeconds() { }
1 アプリケーション開始から2時間後に初めてトリガーが発射されます。
最終的な値は常に1秒単位で丸められます。

@Scheduled#delayed() は、上記のプロパティーのテキスト形式での代替です。ピリオド式は ISO-8601 duration format PnDTnHnMn.nS に基づいており、値は java.time.Duration#parse(CharSequence) で解析されます。ただし、式が数字で始まる場合は、 PT の接頭辞が自動的に追加されます。そのため、例えば PT15S の代わりに 15s を使用することができ、「15 秒」と解析されます。

@Scheduled(every = "2s", delayed = "2h")
void everyTwoSeconds() { }
@Scheduled#delay() がゼロよりも大きい値に設定されている場合、 @Scheduled#delayed() の値は無視されます。

@Scheduled#delay() と比較した場合の主な利点は、値が設定可能であることです。 delay 属性は、デフォルト値やネストされたプロパティ式を含む プロパティ式をサポートしています。(なお、 "{property.path}" スタイルの表現はサポートされていますが、プロパティ式の完全な機能は提供されていません)。

@Scheduled(every = "2s", delayed = "${myMethod.delay.expr}") (1)
void everyTwoSeconds() { }
1 遅延の設定には、configプロパティー myMethod.delay.expr を使用します。

1.4. 同時実行

デフォルトでは、スケジュールされたメソッドは同時に実行することができます。それにもかかわらず、 @Scheduled#concurrentExecution() を通じて同時実行を処理するための戦略を指定することが可能です。

import static io.quarkus.scheduler.Scheduled.ConcurrentExecution.SKIP;

@Scheduled(every = "1s", concurrentExecution = SKIP) (1)
void nonConcurrent() {
  // we can be sure that this method is never executed concurrently
}
1 同時実行はスキップされます。
スケジュールされたメソッドの実行がスキップされると、 io.quarkus.scheduler.SkippedExecution のタイプのCDIイベントが発生します。
同じアプリケーションインスタンス内での実行のみが考慮されることに注意してください。この機能は、クラスター間での動作を意図したものではありません。

1.5. 条件付き実行

@Scheduled#skipExecutionIf() を通じて、スケジュールされたメソッドの実行をスキップするロジックを定義することができます。指定されたBeanクラスは、 io.quarkus.scheduler.Scheduled.SkipPredicate を実装する必要があり、 test() メソッドの結果が true である場合に実行がスキップされます。

class Jobs {

   @Scheduled(every = "1s", skipExecutionIf = MyPredicate.class) (1)
   void everySecond() {
     // do something every second...
   }
}

@Singleton (2)
class MyPredicate implements SkipPredicate {

   @Inject
   MyService service;

   boolean test(ScheduledExecution execution) {
       return !service.isStarted(); (3)
   }
}
1 MyPredicate.class の Bean インスタンスは、実行をスキップすべきかどうかを評価するために使用されます。Beanタイプのセットに指定されたクラスを持つBeanが正確に1つ存在する必要があり、そうでない場合はビルドが失敗します。
2 Beanのスコープは,実行中にアクティブでなければならない。
3 Jobs.everySecond() は、 MyService.isStarted()true を返すまでスキップされます。

なお、これは以下のコードと同等のものです。

class Jobs {

   @Inject
   MyService service;

   @Scheduled(every = "1s")
   void everySecond() {
     if (service.isStarted()) {
        // do something every second...
     }
   }
}

The main idea is to keep the logic to skip the execution outside the scheduled business methods so that it can be reused and refactored easily.

スケジュールされたメソッドの実行がスキップされると、 io.quarkus.scheduler.SkippedExecution のタイプのCDIイベントが発生します。

1.6. Non-blocking Methods

By default, a scheduled method is executed on the main executor for blocking tasks. As a result, a technology that is designed to run on a Vert.x event loop (such as Hibernate Reactive) cannot be used inside the method body. For this reason, a scheduled method that returns java.util.concurrent.CompletionStage<Void> or io.smallrye.mutiny.Uni<Void>, or is annotated with @io.smallrye.common.annotation.NonBlocking is executed on the Vert.x event loop instead.

class Jobs {

   @Scheduled(every = "1s")
   Uni<Void> everySecond() { (1)
     // ...do something async
   }
}
1 The return type Uni<Void> instructs the scheduler to execute the method on the Vert.x event loop.

2. スケジューラー

Quarkusは、 io.quarkus.scheduler.Scheduler 型のビルトインBeanを提供しており、これを注入してスケジューラーを一時停止/再開するために使用することができます。

スケジューラーインジェクションの例
import io.quarkus.scheduler.Scheduler;

class MyService {

   @Inject
   Scheduler scheduler;

   void ping() {
      scheduler.pause(); (1)
      scheduler.pause("myIdentity"); (2)
      if (scheduler.isRunning()) {
         throw new IllegalStateException("This should never happen!");
      }
      scheduler.resume("myIdentity"); (3)
      scheduler.resume(); (4)
      scheduler.getScheduledJobs(); (5)
      Trigger jobTrigger = scheduler.getScheduledJob("myIdentity"); (6)
      if (jobTrigger != null && jobTrigger.isOverdue()){ (7)
        // the job is late to the party.
      }
   }
}
1 すべてのトリガーを一時停止します。
2 スケジュールされた特定のメソッドをそのIDで一時停止する
3 スケジュールされた特定のメソッドを、そのIDで再開する
4 スケジューラーを再開します。
5 List all jobs in the scheduler.
6 Get Trigger metadata for a specific scheduled job by its identity.
7 You can configure the grace period for isOverdue() with quarkus.scheduler.overdue-grace-period

3. プログラムスケジューリング

プログラムでジョブをスケジュールする必要がある場合は、 Quartzエクステンション を追加してQuartz APIを直接使用する必要があります。

Quartz APIを使用したプログラマティックスケジューリング
import org.quartz.Scheduler;

class MyJobs {

    void onStart(@Observes StartupEvent event, Scheduler quartz) throws SchedulerException {
        JobDetail job = JobBuilder.newJob(SomeJob.class)
                .withIdentity("myJob", "myGroup")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "myGroup")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())
                .build();
        quartz.scheduleJob(job, trigger);
    }
}
デフォルトでは、 @Scheduled ビジネスメソッドが見つからない限り、スケジューラーは起動されません。「純粋な」プログラマティックスケジューリングのために、スケジューラーを強制的に起動する必要があるかもしれません。 Quartz Configuration Reference も参照してください。

4. スケジュールされたメソッドとテスト

テストを実行する際には、スケジューラーを無効にすることが望ましいことがよくあります。スケジューラーは、ランタイム設定プロパティー quarkus.scheduler.enabled を通じて無効にすることができます。 false に設定すると、アプリケーションにスケジュールされたメソッドが含まれていても、 スケジューラーは起動しません。特定の テストプロファイル に対してスケジューラーを無効にすることもできます。

5. メトリクス

quarkus.scheduler.metrics.enabledtrue に設定されていて、metrics エクステンションが存在する場合、いくつかの基本的なメトリクスがすぐに公開されます。

Micrometer エクステンションが存在する場合、すべての @Scheduled メソッドに @io.micrometer.core.annotation.Timed インターセプターバインディングが自動的に追加され (すでに存在している場合を除く)、 scheduled.methods という名前の io.micrometer.core.instrument.Timerscheduled.methods.running という名前の io.micrometer.core.instrument.LongTaskTimer が登録されます。タグとしては、宣言したクラスの完全修飾名と、 @Scheduled のメソッド名が使われます。

SmallRye Metrics エクステンションが存在する場合、 @org.eclipse.microprofile.metrics.annotation.Timed インターセプターバインディングがすべての @Scheduled メソッドに自動的に追加され(すでに存在している場合を除く)、 org.eclipse.microprofile.metrics.Timer が各 @Scheduled メソッドに対して作成されます。名前は、宣言したクラスの完全修飾名と @Scheduled メソッドの名前で構成されます。タイマーには、タグ scheduled=true が付いています。

6. 設定リファレンス

ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能

Configuration property

タイプ

デフォルト

The syntax used in CRON expressions.

Environment variable: QUARKUS_SCHEDULER_CRON_TYPE

cron4j, quartz, unix, spring

quartz

Scheduled task metrics will be enabled if a metrics extension is present and this value is true.

Environment variable: QUARKUS_SCHEDULER_METRICS_ENABLED

boolean

false

If schedulers are enabled.

Environment variable: QUARKUS_SCHEDULER_ENABLED

boolean

true

Scheduled task will be flagged as overdue if next execution time is exceeded by this period.

Environment variable: QUARKUS_SCHEDULER_OVERDUE_GRACE_PERIOD

Duration

1S

期間フォーマットについて

期間のフォーマットは標準の java.time.Duration フォーマットを使用します。詳細は Duration#parse() javadoc を参照してください。

数値で始まる期間の値を指定することもできます。この場合、値が数値のみで構成されている場合、コンバーターは値を秒として扱います。そうでない場合は、 PT が暗黙的に値の前に付加され、標準の java.time.Duration 形式が得られます。