本記事はRonald Dehuysserによる“Introducing JobRunr: A distributed job scheduler for Java” を翻訳したものです。
ラムダ式とSpringを活用したオープンソース・ライブラリのクイック・チュートリアル
著者:Ronald Dehuysser
2021年1月15日
JobRunrは、Java 8のラムダ式を使ってバックグラウンド・ジョブをスケジュールすることができるライブラリです。Springサービスの既存の手法を使い、インタフェースを実装することなくジョブを作成することができます。ジョブは短時間で終わるものでも、長時間実行されるものでも構いません。また、バックグラウンド・スレッドに自動的にオフロードされるので、現在のWebリクエストがブロックされることはありません。
JobRunrでは、ジョブを実行するためにJava 8のラムダ式を分析します。また、ラムダ式をJSONにシリアライズし、任意のリレーショナル・データベースやNoSQLデータストアに格納します。
JobRunrで生成されるバックグラウンド・ジョブが多すぎて、サーバーがその負荷に対応できないことに気付いた場合は、アプリケーションのインスタンスを追加するだけで、簡単に水平スケーリングを実行することができます。JobRunrでは、自動的に負荷を共有し、すべてのジョブをアプリケーションの複数のインスタンスに分散します。
さらに、指数バックオフ・ポリシーに従って、失敗したジョブの自動リトライを行う機能も含まれています。すべてのジョブを監視できる、組込みのダッシュボードもあります。JobRunrには自己メンテナンス機能が搭載されており、ジョブが成功すると、設定された時間が経過した後、自動的に削除されます。そのため、手動でストレージをクリーンアップする必要はありません。
詳しくは、GitHubのJobRunrをご覧ください。以下のサンプルのソースはGitHubにアップロードしてあります。このライブラリは、無償で商用利用できます。
JobRunr のセットアップ
Maven 依存性:pom.xmlファイルで、次のMaven依存性を宣言する必要があります。
<dependency>
<groupId>org.jobrunr</groupId>
<artifactId>jobrunr-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
必須プロパティの追加:jobrunr-spring-boot-starter依存性を使用しているので、残りのセットアップ・プロセスは簡単です。application.propertiesに次のプロパティを追加するだけです。
org.jobrunr.background-job-server.enabled=true
org.jobrunr.dashboard.enabled=true
最初のプロパティで、JobRunrにBackgroundJobServerインスタンスを起動することを伝えます。このインスタンスがジョブの処理を担当します。2つ目のプロパティは、JobRunrに対して、組込みダッシュボードを起動するように指示するものです。詳しいドキュメントは、jobrunr.ioに掲載されています。
ジョブに関連するすべての情報をリレーショナル・データベースに保存する場合、jobrunr-spring-boot-starterはデフォルトでは、既存のDataSourceを使おうと試みます。ただし、今回はインメモリ・データストアを使うので、StorageProvider Beanを提供する必要があります。次のようにすると、JobMapper Beanが提供されます。
jobrunr-spring-boot-starter):java @Bean public StorageProvider storageProvider(JobMapper jobMapper) { InMemoryStorageProvider storageProvider = new InMemoryStorageProvider(); storageProvider.setJobMapper(jobMapper); return storageProvider; }
JobRunr の使用
依存性の注入:ジョブを作成するには、jobSchedulerと、ジョブの作成元となる既存サービスを注入します。
```java @Inject private JobScheduler jobScheduler;
@Inject private SampleJobService sampleJobService; ```
ファイア・アンド・フォーゲット型のジョブの作成:依存性を注入したので、enqueueメソッドを使ってファイア・アンド・フォーゲット型のジョブを作成することができます。
jobScheduler.enqueue(() -> sampleJobService.executeSampleJob());
ジョブには、他のラムダ式と同様に、パラメータを含めることができます。次に例を示します。
jobScheduler.enqueue(() -> sampleJobService.executeSampleJob(“some string”));
この裏では何が起こっているのでしょうか。JobRunrでは、ASM Javaバイトコード操作・分析フレームワークを使ってラムダ式を取得し、分析します。そして、適切なクラス(この場合はSampleJobService)と適切なメソッド(この場合はexecuteSampleJob)を抽出し、すべての情報とパラメータを小さなJSONオブジェクトにシリアライズします。
{
"lambdaType": "org.jobrunr.jobs.lambdas.JobLambda",
"className": "com.example.services.SampleJobService",
"methodName": "executeSampleJob",
"jobParameters": [
{
"className": "java.lang.String",
"object": "some string"
}
]
}
StorageProviderでは、この情報をジョブ自体についての追加情報とともにすべてシリアライズし、任意のデータベース(SQLデータベースまたはNoSQLデータベース)に格納します。1つ以上のBackgroundJobServerがStorageProviderを監視し、そこからジョブを取り出します。
BackgroundJobServerではコミット時ロックを使うので、各ジョブは1回だけ処理され、負荷はすべてのBackgroundJobServerで共有されます。この動作は、Kubernetesと相性がよく、アプリケーションのインスタンス数を増やすだけで、水平スケーリングによってジョブ全体の処理速度を高めることができます。
未来のジョブのスケジューリング:未来のジョブは、scheduleメソッドでスケジュールすることができます。
jobScheduler.schedule(() -> sampleJobService.executeSampleJob(),
LocalDateTime.now().plusHours(5));
繰り返しジョブのスケジューリング:繰り返し実行するジョブの場合は、scheduleRecurrentlyメソッドを使います。
jobScheduler.scheduleRecurrently(() -> sampleJobService.executeSampleJob(), Cron.hourly());
@Jobアノテーションの付加:ジョブを完全に制御するには、次に示すように、サービス・メソッドに@Jobアノテーションを付加します。ダッシュボードでの表示名や、ジョブが失敗した場合のリトライ回数を設定できます。
@Job(name = "The sample job with variable %0", retries = 2)
public void executeSampleJob(String variable) {
...
}
表示名には、ジョブに渡される変数を使うこともできます。その場合、String.format()の構文を使います。ある例外が発生した場合にのみ特定のジョブをリトライする必要があるという非常に特殊なユースケースでは、ElectStateFilterを記述することで、ジョブにアクセスして進行を完全に制御することができます。
JobRunr ダッシュボード
JobRunrには、ジョブを監視できる、組込みのダッシュボードが付属しています。http://localhost:8000にアクセスすると、図1に示すように、繰り返しジョブを含め、すべてのジョブを調査することができます。

図1:JobRunrダッシュボード
時には、望ましくない事態も発生します。SSL証明書の有効期限が切れることや、ディスクが満杯になることがあるかもしれません。JobRunrはデフォルトでは、指数バックオフ・ポリシーに従ってバックグラウンド・ジョブを再スケジュールします。バックグラウンド・ジョブは、10回連続で失敗した場合にのみFailed状態になります。
その後、根本原因が解決したときに、失敗したジョブをダッシュボードからキューに入れ直すかどうかを判断することができます。以上の状況は、それぞれのリトライおよびその際の正確なエラー・メッセージ、ジョブの失敗理由を示す完全なスタック・トレースを含め、すべてダッシュボードで確認できます。図2に例を示します。

図2:ジョブに関する詳細
まとめ
本記事では、JobRunrとjobrunr-spring-boot-starterを使った基本的なスケジューラの構築方法について説明しました。このチュートリアルを通して覚えていただきたいことは、XMLベースの構成を使ったり、インタフェースを実装したりすることなく、1行のコードだけでジョブを作成できるということです。
サンプルのソース・コードはすべて、GitHubからダウンロードできます。
さらに詳しく
Ronald Dehuysser:ベルギーのソフトウェア・アーキテクトおよび開発者。ユーザー・フレンドリで画期的なWebベースのエンタープライズ・アプリケーションの設計、開発に従事。開発者、アナリスト、指導者として、IT業界で15年超の経験を有す。フォローは@rdehuyssから。
