1. 概述
这篇文章将带你了解如何在 Java 应用中通过 JobRunr 来实现分布式后台任务调度和处理,并与 Spring 框架无缝集成。
2. 关于 JobRunr
JobRunr 是一个可以嵌入到我们应用中的轻量级库,支持使用 Java 8 Lambda 表达式来定义后台任务。你可以直接使用已有的 Spring Service 方法创建任务,无需额外实现接口。这些任务可以是短时或长时运行的,都会被自动放到后台线程执行,避免阻塞当前的 Web 请求。
JobRunr 会分析传入的 Lambda 表达式,将其序列化为 JSON 并存储到关系型数据库或 NoSQL 数据库中。
3. JobRunr 的核心特性
如果你发现后台任务太多导致服务器压力过大,可以通过 横向扩展 来轻松应对:只需新增应用实例即可。JobRunr 会自动分配负载,把任务均匀分发到不同节点上。
它还内置了失败任务的 指数退避自动重试机制 和一个 可视化监控面板(Dashboard)。JobRunr 还能自我维护:成功执行的任务会在一段时间后自动清理,无需手动干预。
4. 环境搭建
为了简化演示,这里我们使用内存数据存储(in-memory storage)来保存任务信息。
4.1. Maven 配置
首先,在你的 pom.xml
文件中添加如下依赖:
<dependency>
<groupId>org.jobrunr</groupId>
<artifactId>jobrunr-spring-boot-starter</artifactId>
<version>5.1.7</version>
</dependency>
4.2. Spring 集成配置
由于我们使用的是 jobrunr-spring-boot-starter
,集成非常简单。只需要在 application.properties
中添加以下配置:
org.jobrunr.background-job-server.enabled=true
org.jobrunr.dashboard.enabled=true
第一条配置表示启用一个 BackgroundJobServer
实例用于处理任务;第二条则是开启内置的 Dashboard。
默认情况下,JobRunr 会尝试使用你项目中已有的 DataSource
来存储任务信息。
但因为我们使用的是内存存储,所以需要提供一个 StorageProvider
Bean:
@Bean
public StorageProvider storageProvider(JobMapper jobMapper) {
InMemoryStorageProvider storageProvider = new InMemoryStorageProvider();
storageProvider.setJobMapper(new JobMapper(new JacksonJsonMapper()));
return storageProvider;
}
5. 使用方式
接下来我们看看如何在 Spring 中使用 JobRunr 创建和调度后台任务。
5.1. 注入依赖
我们需要注入 JobScheduler
和目标 Spring Service(比如 SampleJobService
),如下所示:
@Inject
private JobScheduler jobScheduler;
@Inject
private SampleJobService sampleJobService;
其中:
JobScheduler
负责入队或调度任务;SampleJobService
可以是你任意一个耗时较长或需要容错的服务类。
5.2. 创建一次性任务(Fire-and-Forget)
使用 enqueue()
方法可以快速提交一个一次性任务:
jobScheduler.enqueue(() -> sampleJobService.executeSampleJob());
也可以带参数:
jobScheduler.enqueue(() -> sampleJobService.executeSampleJob("some string"));
JobRunr 会将这个 Lambda 表达式的类型、方法名、参数等信息序列化并持久化到存储中。之后,由多个 BackgroundJobServer
中的线程池按先进先出顺序执行这些任务。✅ JobRunr 通过乐观锁确保每个任务只会被一个 Worker 执行一次。
5.3. 定时调度任务
如果想在未来某个时间点执行任务,可以用 schedule()
方法:
jobScheduler.schedule(LocalDateTime.now().plusHours(5), () -> sampleJobService.executeSampleJob());
5.4. 创建周期性任务
使用 scheduleRecurrently()
方法可创建定时重复任务:
jobScheduler.scheduleRecurrently(Cron.hourly(), () -> sampleJobService.executeSampleJob());
5.5. 使用 @Job 注解控制任务行为
可以通过给方法加上 @Job
注解来自定义任务行为,例如设置 Dashboard 显示名称、重试次数等:
@Job(name = "The sample job with variable %0", retries = 2)
public void executeSampleJob(String variable) {
...
}
其中 %0
表示第一个参数,支持类似 String.format()
的语法。
⚠️ 如果你有特殊需求,比如只在特定异常下重试任务,可以自定义 ElectStateFilter
类来精细控制任务状态流转。
6. 内置 Dashboard 监控面板
JobRunr 提供了一个内置的 Dashboard,访问地址为:http://localhost:8000
在这里可以看到所有任务的状态,包括排队中的、正在执行的、失败的以及周期性任务,并能预估任务完成时间。
当任务因网络、证书过期等问题失败时,JobRunr 默认会使用指数退避策略自动重试,最多重试 10 次后才会标记为失败。此时你可以在 Dashboard 上查看详细的错误信息和堆栈跟踪:
7. 总结
本文展示了如何使用 jobrunr-spring-boot-starter
快速搭建一个基本的任务调度系统。关键在于仅需一行代码就可以创建任务,完全不需要 XML 配置或实现特定接口,真正做到开箱即用。
完整代码示例可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-libraries-2