はじめに
SpringFrameworkには、起動しているアプリケーションから非同期の処理を実行できる機能が提供されており、非同期処理を実行する方法がいくつかあります。その中の1つである スケジューラー @Scheduled
を使った定期実行の方法と、そのスケジューラーの多重起動の防止を行うShedLockを紹介します。
スケジューラー機能の使い方
SpringBootを使った設定は以下の通りです
- アプリケーション起動クラスないしは設定クラスに
@EnableScheduling
を付与する - 一定間隔で非同期実行をするメソッドに
@Scheduled
を付与する(Springのコンテキストに登録してあるクラスのみ対象。)
具体的なコード例は次の通りです。
アプリケーション起動クラス、ないしは設定クラスにて@EnableScheduling
を付与しておけば、あとはスケジュール実行したいメソッドに @Scheduled
を付与するだけです。
@Scheduled
に指定できる属性にはcrontabと同じ形式を設定するcronか、アプリケーション起動完了後からの実行間隔を指定する方法が選べます。この例では、10秒間隔で registerNormal() メソッドが非同期で実行されます。
cronの指定では時刻のタイムゾーンも別途指定できます。省略時はアプリケーションが起動しているサーバのタイムゾーン1が使われます。
懸念点
常に非同期で実行するということは、先程の例では、10秒間隔でメソッドが動作します。
このアプリケーションが単一のアプリケーションで動作していて、かつこのメソッドで実行される処理が常に10秒以内で完了すれば特に問題ないところですが、作成するアプリケーションの大きさによっては、複数のサーバインスタンスに同一のアプリケーションが配備されて運用するケースがあります。
そうなると、時間指定をした @Scheduled
は、複数のアプリケーションから同時に同じ処理実行されることになるでしょう。検索処理や状態監視なら良いのですが、何らかのデータ登録や更新がある場合は、アプリケーション内で排他処理を実施しなければ重複することになるでしょう。
解決方法の1つ :ShedLockの導入
複数実行している同一アプリケーションで排他処理をかんたんに実装できるライブラリに、https://github.com/lukas-krecan/ShedLock があります。
これは複数実行されている @Scheduled
に対して排他処理を追加するものです。使い方もSpringBootのアノテーションのようにするだけです。
ShedLockの導入に必要なもの
導入に必要なものは以下です。
- ShedLock本体(maven centralないしは前述したgithubのリポジトリから入手)
- ShedLock用のデータベース(JDBCから接続できるRDBや、Redis、DynamoDBなど多数から選択可。SpringBootアプリケーションにて定義済みのデータソースを利用しています)
- ShedLockが扱うデータベースのテーブルを1つ作成
これらを準備して、次は設定するだけです。
ShedLockの設定
Springの @Scheduled
同様に、アプリケーション設定用アノテーションと、排他を実施したいスケジューラーで起動しているメソッドにアノテーションを付与します。
@EnableSchedulerLock
で、ShedLockの利用を宣言します。このアノテーションの属性で、@Scheduled
で実行するスケジューリングしたメソッドの排他時間のデフォルト値を設定します。
ShedLockを適用したサンプルコードスケジュール起動するメソッドのうち、排他を実施したいものに @SchedulerLock
を付与します。
name属性はアプリケーション内で唯一となるものをつけます。ShedLockはこのname属性に指定した名前で排他する処理であるかを認識し、メソッドの開始をしたと同時に排他を開始します。
排他の終了はShedLockの設定で定義した排他時間を超過しているかで判断します。
排他時間の設定方法
以下の2つを設定できます。
属性名 | 解説 |
---|---|
lockAtLeastFor(またはlockAtLestForString) | 排他開始から最低でも排他を続ける時間。メソッドが終了してもこの時間までは排他がかかり続ける。排他中はスケジューラからの起動はされずに破棄される |
lockAtMostFor(またはlockAtMostForString) | 排他開始から最大で排他を続ける時間。メソッドが終了していなくてもこの時間が到達した場合は排他が解除される |
この2つの値は @EnableSchedulerLock
にて設定したdefault設定を、個別のスケジューラにて上書きできます。
まとめ
アプリケーションレベルでかんたんにスケジューラと排他を実施できることがわかりました。大掛かりなスケジューリング機能やサービスを使わずとも実現できますので、まずは手軽に処理単位が小さいものに適用してみるのも良いかと思います。
サンプルソース
以下に公開しております。
サンプルではデータベースにMySQLを選んでいます。
-
タイムゾーンが影響する設定、具体的には 1時間単位で指定して実行する処理を
@Scheduled
で設定した内容だけでアプリケーションの機能が充足するのかどうかはアプリケーションの設計で判断すること ↩