如何在Spring中使用JobRunr实现后台作业? - Baeldung


在本教程中,我们将研究使用JobRunr在Java中进行分布式后台作业调度和处理,并将其与Spring集成。
JobRunr是一个我们可以嵌入到应用程序中的库,它使我们可以使用Java 8 lambda调度后台作业。我们可以使用Spring服务的任何现有方法来创建作业,而无需实现接口。作业可能是一个短期或长期运行的过程,它将被自动卸载到后台线程,这样就不会阻止当前的Web请求。JobRunr分析了Java 8 lambda。它将其序列化为JSON,并将其存储到关系数据库或NoSQL数据存储中。
如果我们看到我们产生了太多的后台作业,而我们的服务器无法应付负载,则可以通过添加应用程序的额外实例来轻松地水平扩展。JobRunr将自动分担负载,并在我们应用程序的不同实例上分配所有作业。
它还包含自动重试功能,并针对失败的作业提供指数补偿策略。还有一个内置的仪表板,使我们可以监视所有作业。JobRunr是自我维护的–成功的作业将在可配置的时间后自动删除,因此无需执行手动存储清理。
 
设置

<dependency>
    <groupId>org.jobrunr</groupId>
    <artifactId>jobrunr-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

只需要向application.properties添加一些属性:
org.jobrunr.background_job_server=true
org.jobrunr.dashboard=true

第一个属性告诉JobRunr我们要启动一个负责处理作业的BackgroundJobServer实例。第二个属性告诉JobRunr启动嵌入式仪表板。
默认情况下,如果是关系数据库来存储所有与工作相关的信息,jobrunr-spring-boot-starter会尝试使用您现有的数据源。
但是,由于我们将使用内存中的数据存储,因此我们需要提供一个StorageProvider bean:
@Bean
public StorageProvider storageProvider(JobMapper jobMapper) {
    InMemoryStorageProvider storageProvider = new InMemoryStorageProvider();
    storageProvider.setJobMapper(jobMapper);
    return storageProvider;
}

 
用法
当我们要创建作业时,我们需要注入JobScheduler和我们现有的Spring服务,其中包含我们要为其创建作业的方法,在本例中为SampleJobService:
@Inject
private JobScheduler jobScheduler;
 
@Inject
private SampleJobService sampleJobService;

jobscheduler从JobRunr类允许我们排队或安排新的后台作业。
SampleJobService可能是我们任何包含可能需要很长时间的web请求处理方法存在的Spring服务。它也可以是在其他我们想添加弹性的外部服务上调用的方法,因为如果发生异常,JobRunr将重试该方法。
现在我们有了依赖关系,我们可以使用enqueue方法创建即发即弃的作业:

jobScheduler.enqueue(() -> sampleJobService.executeSampleJob());

作业可以具有参数,就像其他任何lambda一样:

jobScheduler.enqueue(() -> sampleJobService.executeSampleJob("some string"));

将lambda(包括类型,方法和参数)作为JSON序列化到持久性存储(Oracle,Postgres,MySql和MariaDB之类的RDBMS或NoSQL数据库)。
然后,在所有不同BackgroundJobServer中运行的专用线程工作池将以先进先出的方式尽快执行这些排队的后台作业。JobRunr通过乐观锁定保证单个工人执行您的工作。
还可以使用schedule方法来计划将来的作业:
jobScheduler.schedule(() -> sampleJobService.executeSampleJob(), LocalDateTime.now().plusHours(5));

如果我们想拥有周期性工作,则需要使用scheduleRecurrently方法:
jobScheduler.scheduleRecurrently(() -> sampleJobService.executeSampleJob(), Cron.hourly());

为了控制作业的各个方面,我们可以使用@Job注释来注释我们的服务方法。这样可以在仪表板中设置显示名称,并配置万一作业失败的重试次数。

@Job(name = "The sample job with variable %0", retries = 2)
public void executeSampleJob(String variable) {
    ...
}

我们甚至可以使用通过String.format()语法以显示名称传递给作业的变量。
如果我们有非常特殊的用例,而我们只想在特定的例外情况下重试特定的作业,则可以编写自己的ElectStateFilter,在其中可以访问Job并完全控制操作方式。
 
仪表板
JobRunr带有内置的仪表板,可让我们监视作业。我们可以在http:// localhost:8000上找到它,并检查所有作业,包括所有重复出现的作业,以及在处理所有排队的作业之前需要多长时间的估计。
可能会发生不良情况,例如SSL证书过期或磁盘已满。默认情况下,JobRunr将使用指数退避策略重新计划后台作业。如果后台作业继续失败十次,则只有这样才能进入失败状态。解决了根本原因后,您可以决定重新排队失败的作业。
所有这些信息在仪表板中都是可见的,包括每次重试以及确切的错误消息和作业失败原因的完整堆栈跟踪:
 
在本文中,我们使用Jobrunr和jobrunr-spring-boot-starter构建了第一个基本调度程序。本教程的主要收获是,我们仅用一行代码即可创建作业,而无需进行任何基于XML的配置或实现接口。
该示例的完整源代码可在GitHub上获得