【Spring Boot】@Scheduledでバッチ処理を実装する方法【排他制御まで解説】
Spring BootでWebアプリケーションを開発していると、API処理だけでなく、定期的に実行したい処理が必要になることがあります。
例えば、以下のような処理です。
・5分おきに未処理データを確認する
・15分おきに外部APIと連携する
・毎日深夜に集計処理を実行する
・古いデータを定期的に削除する
このような定期実行処理は、一般的にバッチ処理と呼ばれます。Spring Bootでは、@Scheduled を使うことで、比較的簡単にバッチ処理を実装できます。この記事では、初心者の方でも実装できるように、@Scheduled の基本的な使い方から、ShedLockを使った排他制御まで解説します。
@Scheduledとは
@Scheduled は、Spring Frameworkが提供している定期実行用のアノテーションです。メソッドに @Scheduled を付けることで、指定した間隔や時刻で処理を自動実行できます。
たとえば、15分おきに処理を実行したい場合は、以下のように書きます。
@Scheduled(cron = "0 */15 * * * *", zone = "Asia/Tokyo")
public void execute() {
// 15分おきに実行したい処理
}
cron式は 秒 分 時 日 月 曜日 の順で指定します。 0 */15 * * * * は、毎時0分、15分、30分、45分に実行するという意味です。
@Scheduledを有効化する
@Scheduled を使うには、設定クラスに @EnableScheduling を付けます。
@Configuration
@EnableScheduling
public class SchedulerConfig {
}
これで、Springが @Scheduled の付いたメソッドを検出して、定期実行してくれるようになります。
シンプルなバッチ処理の実装例
まずは、15分おきに実行されるシンプルなバッチを作成します。
@Component
public class SampleBatchScheduler {
private final SampleBatchService sampleBatchService;
@Scheduled(cron = "0 */15 * * * *", zone = "Asia/Tokyo")
public void execute() {
sampleBatchService.execute();
}
}
実際の処理は SampleBatchService に分けます。
@Service
public class SampleBatchService {
public void execute() {
// バッチ本体の処理を書く
}
}
複数インスタンスでは二重実行に注意
本番環境では、Spring Bootアプリを複数台で動かすことがあります。この状態で @Scheduled を使うと、各サーバーで同じバッチが実行される可能性があります。同じ処理が重複して実行されないように、ShedLockを使って排他制御を行います。
ShedLockとは
ShedLockは、Springのスケジューラに分散ロックを追加するライブラリです。複数のインスタンスがあっても、同じバッチを1台だけが実行するようにできます。ロックを取得できなかった場合、処理を待たずにスキップするため、二重実行を防げます。
ShedLockを使うには、依存関係を追加し、ロック用のテーブルを作成します。MySQLの場合は、下記のようなSQLを実行しておきます。
CREATE TABLE shedlock (
name VARCHAR(64) NOT NULL,
lock_until TIMESTAMP(3) NOT NULL,
locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
locked_by VARCHAR(255) NOT NULL,
PRIMARY KEY (name)
);
設定クラスで @EnableSchedulerLock を有効にし、LockProvider を設定します。
@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "30m")
public class SchedulerConfig {
}
@Configuration
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(
JdbcTemplateLockProvider.Configuration.builder()
.withJdbcTemplate(new JdbcTemplate(dataSource))
.usingDbTime()
.build()
);
}
}
ShedLockを使った実装例
ShedLockを組み合せて15分バッチを実装すると、以下のようになります。
@Component
public class SampleBatchScheduler {
private final SampleBatchService sampleBatchService;
@Scheduled(cron = "0 */15 * * * *", zone = "Asia/Tokyo")
@SchedulerLock(
name = "SampleBatchScheduler.execute",
lockAtMostFor = "30m",
lockAtLeastFor = "14m"
)
public void execute() {
sampleBatchService.execute();
}
}
name はロック名です。同じ名前のタスクは同時に実行されません。lockAtMostFor はロックの最大保持時間で、アプリが強制終了した場合でも期限切れでロックが解放されます。lockAtLeastFor は最位保持時間で、短い間隔で連続実行されるのを防ぎたい場合に指定しま。
複数のバッチがある場合の注意点
同じアプリ内に5分バッチ、15分バッチを含める場合は、実行タイミングをずらしたり、ロック名を分けるなどの工夫が必要です。また、スケジューラのスレッド数やDB接続数も調整し、API処理とバッチ処理がリソースを奪い合わないようにしましょう。
是非フォローしてください
最新の情報をお伝えします
