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

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

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

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

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

メソッドに @io.quarkus.scheduler.Scheduled をアノテーションすると、自動的に呼び出されるようにスケジュールされます。実際には、そのようなメソッドはCDI Beanの非プライベート非静的メソッドでなければなりません。CDI Beanのメソッドであることの結果として、スケジュールされたメソッドは、 @javax.transaction.Transactional@org.eclipse.microprofile.metrics.annotation.Counted のようなインターセプターバインディングでアノテーションすることができます。

スコープを持たないBeanクラスで、 @Scheduled でアノテーションされた非静的メソッドが少なくとも一つ宣言されている場合、 @Singleton が使用されます。

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

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

サブクラスは、スーパークラスで宣言された @Scheduled メソッドのメタデータを継承することはありません。次の例では、 everySecond() メソッドは Jobs のインスタンスに対してのみ呼び出されます。

class Jobs {

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

@Singleton
class MyJobs extends Jobs {
}

io.quarkus.scheduler.SuccessfulExecution 型の CDI イベントは、スケジュールされたメソッドの実行が成功したときに、同期および非同期で発生します。 io.quarkus.scheduler.FailedExecution タイプの CDI イベントは、スケジュールされたメソッドの実行が例外をスローしたときに、同期および非同期で発生します。

1.1. トリガー

トリガーは、 @Scheduled#cron() 属性または @Scheduled#every() 属性によって定義されます。両方が指定されている場合、cron 式が優先されます。何も指定されていない場合、ビルドは 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. アイデンティティ

デフォルトでは、スケジュールされた各メソッドに対して一意の ID が生成されます。このIDはログメッセージやデバッグ中に使用されます。時には、明示的な id を指定することが便利な場合もあります。

アイデンティティの例
@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...
     }
   }
}

主なアイデアは、スケジュールされたビジネスメソッドの外で実行をスキップするロジックを維持することで、再利用やリファクタリングが容易にできるようにすることです。

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

1.6. ノンブロッキング方式

デフォルトでは、スケジュールされたメソッドは、ブロックするタスクのメインエクゼキュータ上で実行されます。そのため、Vert.xイベントループ上で実行するように設計された技術(Hibernate Reactiveなど)は、メソッド本体内で使用することができません。このため、 java.util.concurrent.CompletionStage<Void> または io.smallrye.mutiny.Uni<Void> を返す、あるいは @io.smallrye.common.annotation.NonBlocking でアノテーションされたスケジュール型メソッドは、代わりに Vert.x のイベントループで実行されます。

class Jobs {

   @Scheduled(every = "1s")
   Uni<Void> everySecond() { (1)
     // ...do something async
   }
}
1 リターンタイプ Uni<Void> は、Vert.xのイベントループでメソッドを実行するようにスケジューラに指示します。

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 スケジューラーを再開します。
6 スケジュールされた特定のメソッドを、そのIDで再開する。
7 isOverdue()の猶予期間は、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設定リファレンス も参照してください。

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. OpenTelemetry Tracing

If quarkus.scheduler.tracing.enabled is set to true and the OpenTelemetry extension is present then the @io.opentelemetry.instrumentation.annotations.WithSpan annotation is added automatically to every @Scheduled method. As a result, each execution of this method has a new io.opentelemetry.api.trace.Span associated.

Non-blocking methods are not supported, i.e. a new span is associated with the actual invocation but it’s not available within the asynchronous computation.

7. 設定リファレンス

ビルド時に固定される設定プロパティ - その他の設定プロパティはランタイムでオーバーライド可能です。

Configuration property

デフォルト

The syntax used in CRON expressions.

Environment variable: QUARKUS_SCHEDULER_CRON_TYPE

cron4j, quartz, unix, spring, spring53

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

Tracing will be enabled if the OpenTelemetry extension is present and this value is true.

Environment variable: QUARKUS_SCHEDULER_TRACING_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 形式が得られます。