概要
DBサーバで外部スケジューラからShellを起動し、Shell内でjarファイルを呼び出すバッチ処理を運用していました。今回、Quartz Job Schedulerを利用してJavaアプリでスケジュール実行する方式に変更しました。その際の実装方法やハマりやすいポイントをまとめます。
経緯
元々、Webとバッチ処理が一つのAntビルドJavaプロジェクトにまとまっていました。Maven移行時、バッチ処理は移行されず、jarファイルのみ残存していたところ、Quartz Job SchedulerがJavaで利用できることを知り、既存コードを活かしてシンプルな設計にできないか検討しました。
結果、JavaアプリでQuartzを使ってスケジュール実行する構成に変更。
実装方法
ServletにQuartz Job Schedulerを組み込み
- JobBuilderでJobClassを設定(JobClassはJarファイルのものを流用)
- Triggerで起動時間を設定
- Schedulerを起動
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
TimeZone tz = TimeZone.getTimeZone("Asia/Tokyo");
JobDetail sampleJob = JobBuilder.newJob(SampleJob.class)
.withIdentity("sampleJob", "sampleGroup")
.build();
Trigger trigger = newTrigger()
.withIdentity("sampleTrigger", "sampleGroup")
// 毎週土曜 午前1時に実行
.withSchedule(cronSchedule("0 0 1 ? * SAT").inTimeZone(tz))
.forJob("sampleJob", "sampleGroup")
.build();
scheduler.scheduleJob(sampleJob, trigger);
// アプリ終了時にSchedulerを停止
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
scheduler.shutdown(true);
} catch (SchedulerException e) {
//エラー処理
}
}));
} catch (SchedulerException e) {
//エラー処理
付け加えて、毎朝メールでJOB結果を送信するJOBを追加し、Schedulerに登録してJOB結果確認を楽にしました。
ハマりやすいポイントと対処法
- タイムゾーンのズレ
Triggerはサーバのシステム時間を参照するため、タイムゾーンがずれると意図しない時間に実行されてしまいます。
対策:cronSchedule(...).inTimeZone(tz)で明示的にタイムゾーン設定。
ポイント:ローカルとサーバの時間差がある場合は必ずタイムゾーンを指定。
- 実行時間が長くなる問題
クラウド環境にあるアプリサーバからDBサーバへのアクセスで、従来よりJOBが長時間化。
原因:大量データを取得し、forで1件ずつUPDATEする非効率ロジック。
対策:SQLや処理ロジックを見直し、まとめて更新する方式に変更。
教訓:普段触らない古いコードは、こういうタイミングで問題化するので要注意。
- 権限管理
当然ですが一般ユーザがJOB実行できてしまうとまずいので、JOB実行権限を持つ人にのみ権限を付与して、アクセス制御する必要があります。
- Log
ABEND時に迅速な対応ができるよう、JOB Logには詳細な情報を記載しておきましょう。
まとめ
Quartz Job Schedulerを使うことで、既存コードを流用しつつ管理しやすい構成にすることができました。公式ドキュメントは英語ですが、チュートリアルやサンプルが充実していて比較的導入しやすいのではないかと思われます。