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 overview

当任务因网络、证书过期等问题失败时,JobRunr 默认会使用指数退避策略自动重试,最多重试 10 次后才会标记为失败。此时你可以在 Dashboard 上查看详细的错误信息和堆栈跟踪:

jobrunr baeldung

7. 总结

本文展示了如何使用 jobrunr-spring-boot-starter 快速搭建一个基本的任务调度系统。关键在于仅需一行代码就可以创建任务,完全不需要 XML 配置或实现特定接口,真正做到开箱即用。

完整代码示例可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-libraries-2


原始标题:Background Jobs in Spring with JobRunr | Baeldung