Автор оригинала: mkyong.
В этой статье показано, как использовать Spring Boot для создания конечных точек REST для выполнения фоновых заданий, управляемых Jobrunner .
Протестировано с:
- Пружинный ботинок 2.3.12.ВЫПУСК
- ДжоБрун 3.1.2
- Мавен 3
1. Структура каталогов.
Стандартная структура проекта Maven.
2. Зависимости проекта.
Нам нужен один jobrunner-spring-boot-starter для интеграции Spring Boot web и Jobrunner.
org.jobrunr jobrunr-spring-boot-starter 1.2.0 org.springframework.boot spring-boot-starter-web
Просмотрите зависимости JobRunr , это зависит от ASM и SLF4J .
$ mvn dependency:tree [INFO] +- org.jobrunr:jobrunr-spring-boot-starter:jar:3.1.2:compile [INFO] | \- org.jobrunr:jobrunr:jar:3.1.2:compile [INFO] | +- org.slf4j:slf4j-api:jar:1.7.30:compile [INFO] | \- org.ow2.asm:asm:jar:9.1:compile
На самом деле, для Выполнения задания также нужна одна из библиотек JSON ( Джексон , ГСОН или Json-B ) для отображения в формате JSON. В этом примере мы можем игнорировать библиотеку JSON, потому что spring-boot-starter-web предоставляет библиотеку Джексона по умолчанию.
Просмотрите все зависимости проекта.
$ mvn dependency:tree [INFO] Scanning for projects... [INFO] [INFO] -------------------< com.mkyong:spring-boot-jobrunr >------------------- [INFO] Building spring-boot-jobrunr 1.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ spring-boot-jobrunr --- [INFO] com.mkyong:spring-boot-jobrunr:jar:1.0 [INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.3.12.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.3.12.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot:jar:2.3.12.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.3.12.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.3.12.RELEASE:compile [INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile [INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile [INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile [INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.13.3:compile [INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.30:compile [INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile [INFO] | | \- org.yaml:snakeyaml:jar:1.26:compile [INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.3.12.RELEASE:compile [INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.11.4:compile [INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.11.4:compile [INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.11.4:compile [INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.11.4:compile [INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.11.4:compile [INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.11.4:compile [INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.3.12.RELEASE:compile [INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.46:compile [INFO] | | +- org.glassfish:jakarta.el:jar:3.0.3:compile [INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.46:compile [INFO] | +- org.springframework:spring-web:jar:5.2.15.RELEASE:compile [INFO] | | \- org.springframework:spring-beans:jar:5.2.15.RELEASE:compile [INFO] | \- org.springframework:spring-webmvc:jar:5.2.15.RELEASE:compile [INFO] | +- org.springframework:spring-aop:jar:5.2.15.RELEASE:compile [INFO] | +- org.springframework:spring-context:jar:5.2.15.RELEASE:compile [INFO] | \- org.springframework:spring-expression:jar:5.2.15.RELEASE:compile [INFO] +- org.jobrunr:jobrunr-spring-boot-starter:jar:3.1.2:compile [INFO] | \- org.jobrunr:jobrunr:jar:3.1.2:compile [INFO] | +- org.slf4j:slf4j-api:jar:1.7.30:compile [INFO] | \- org.ow2.asm:asm:jar:9.1:compile [INFO] +- org.springframework.boot:spring-boot-starter-test:jar:2.3.12.RELEASE:test [INFO] | +- org.springframework.boot:spring-boot-test:jar:2.3.12.RELEASE:test [INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.3.12.RELEASE:test [INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:test [INFO] | | \- net.minidev:json-smart:jar:2.3.1:test [INFO] | | \- net.minidev:accessors-smart:jar:2.3.1:test [INFO] | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:test [INFO] | | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:test [INFO] | +- org.assertj:assertj-core:jar:3.16.1:test [INFO] | +- org.hamcrest:hamcrest:jar:2.2:test [INFO] | +- org.junit.jupiter:junit-jupiter:jar:5.6.3:test [INFO] | | +- org.junit.jupiter:junit-jupiter-api:jar:5.6.3:test [INFO] | | | +- org.apiguardian:apiguardian-api:jar:1.1.0:test [INFO] | | | +- org.opentest4j:opentest4j:jar:1.2.0:test [INFO] | | | \- org.junit.platform:junit-platform-commons:jar:1.6.3:test [INFO] | | +- org.junit.jupiter:junit-jupiter-params:jar:5.6.3:test [INFO] | | \- org.junit.jupiter:junit-jupiter-engine:jar:5.6.3:test [INFO] | | \- org.junit.platform:junit-platform-engine:jar:1.6.3:test [INFO] | +- org.mockito:mockito-core:jar:3.3.3:test [INFO] | | +- net.bytebuddy:byte-buddy:jar:1.10.22:test [INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.10.22:test [INFO] | | \- org.objenesis:objenesis:jar:2.6:test [INFO] | +- org.mockito:mockito-junit-jupiter:jar:3.3.3:test [INFO] | +- org.skyscreamer:jsonassert:jar:1.5.0:test [INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test [INFO] | +- org.springframework:spring-core:jar:5.2.15.RELEASE:compile [INFO] | | \- org.springframework:spring-jcl:jar:5.2.15.RELEASE:compile [INFO] | +- org.springframework:spring-test:jar:5.2.15.RELEASE:test [INFO] | \- org.xmlunit:xmlunit-core:jar:2.7.0:test [INFO] \- org.awaitility:awaitility:jar:4.0.3:test [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.699 s [INFO] Finished at: 2021-06-20T14:13:37+08:00 [INFO] ------------------------------------------------------------------------
3. Настройка JobRunr
Для приложения Spring Boot мы можем настроить Бегун заданий с помощью файла application.properties .
org.jobrunr.background-job-server.enabled=true org.jobrunr.dashboard.enabled=true
- Свойство
org.jobrunr.background-задание-сервер.включено=trueуказываетJobRunrчтобы начатьBackgroundJobServerэкземпляр для обработки заданий. -
org.jobrunr.dashboard.enabled=истинасообщаетБегун заданийдля запуска встроенной панели мониторинга для отслеживания состояния заданий.
Дополнительная документация доступна на jobrunr.io
4. Хранилище JobRunr
Исполнителю заданий требуется хранилище для хранения сведений о задании и поддержки основные базы данных SQL и базы данных NoSQL .
В этом примере мы используем хранилище данных в памяти для хранения сведений о задании.
package com.mkyong;
import org.jobrunr.jobs.mappers.JobMapper;
import org.jobrunr.storage.InMemoryStorageProvider;
import org.jobrunr.storage.StorageProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfiguration {
// The`spring-boot-starter-web` provides Jackson as JobMapper
@Bean
public StorageProvider storageProvider(JobMapper jobMapper) {
InMemoryStorageProvider storageProvider = new InMemoryStorageProvider();
storageProvider.setJobMapper(jobMapper);
return storageProvider;
}
}
5. Создание и запуск задания Jobrunner
5.1 Ниже приведен простой компонент Spring, управляемый @Service для регистрации предоставленного сообщения. И мы можем использовать @Job для определения имени задания (это будет отображаться на панели управления Jobrunner).
package com.mkyong.job;
import org.jobrunr.jobs.annotations.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class SampleJobService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Job(name = "The sample job without variable")
public void execute() {
execute("Hello world!");
}
@Job(name = "The sample job with variable %0")
public void execute(String input) {
logger.info("The sample job has begun. The variable you passed is {}", input);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
logger.error("Error while executing sample job", e);
} finally {
logger.info("Sample job has finished...");
}
}
}
5.2 Нам нужен Планировщик заданий экземпляр для выполнения заданий.
import org.jobrunr.scheduling.JobScheduler; @Autowired // or @Inject private JobScheduler jobScheduler; @Autowired // or @Inject private SampleJobService sampleJobService;
5.3 Выполнить задание “пожар и забыть” (одноразовое задание)
jobScheduler.enqueue( () -> sampleJobService.executeSampleJob());
5.4 Запустите задание, поддерживающее параметры.
jobScheduler.enqueue(
() -> sampleJobService.executeSampleJob("some string"));
5.5 Планирование работы в будущем.
// Old APIs, jobs followed by time /*jobScheduler.schedule( () -> sampleJobService.executeSampleJob(), LocalDateTime.now().plusHours(5));*/ // new APIs, time first, followed by jobs (to be able to support Kotlin) jobScheduler.schedule( LocalDateTime.now().plusHours(5), () -> sampleJobService.executeSampleJob());
5.6 Планирование задания периодически, выполняйте его каждый час.
jobScheduler.scheduleRecurrently( Cron.hourly(), () -> sampleJobService.executeSampleJob());
6. Конечные точки пружинного ОТДЫХА
Ниже приведен пример загрузки Spring для создания следующих конечных точек:
/выполнить-заданиеконечная точка для немедленного выполнения задания./расписание-заданиеконечная точка для планирования выполнения задания в будущем.
package com.mkyong.api;
import com.mkyong.job.SampleJobService;
import org.jobrunr.scheduling.JobScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.time.Instant;
@RestController
public class JobController {
@Autowired
private JobScheduler jobScheduler;
@Autowired
private SampleJobService sampleJobService;
@GetMapping("/run-job")
public String runJob(
@RequestParam(value = "name", defaultValue = "Hello World") String name) {
jobScheduler.enqueue(() -> sampleJobService.execute(name));
return "Job is enqueued.";
}
@GetMapping("/schedule-job")
public String scheduleJob(
@RequestParam(value = "name", defaultValue = "Hello World") String name,
@RequestParam(value = "when", defaultValue = "PT3H") String when) {
jobScheduler.schedule(
Instant.now().plus(Duration.parse(when)),
() -> sampleJobService.execute(name)
);
return "Job is scheduled.";
}
}
P.S В Продолжительность , то PT3H (PT3H) означает 3 часа, a Стандарт ISO 8601 стандарт
7. Приложение Для Весенней Загрузки.
Ниже приведено основное приложение Spring Boot для запуска всего.
package com.mkyong;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(MainConfiguration.class)
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Выход
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.12.RELEASE) o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) o.apache.catalina.core.StandardService : Starting service [Tomcat] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1364 ms o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' j.s.t.ScheduledThreadPoolJobRunrExecutor : ThreadManager of type 'ScheduledThreadPool' started o.j.dashboard.JobRunrDashboardWebServer : JobRunr Dashboard started at http://localhost:8000/dashboard o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' com.mkyong.MainApplication : Started MainApplication in 2.527 seconds (JVM running for 3.094) org.jobrunr.server.BackgroundJobServer : JobRunr BackgroundJobServer (eddcedcf-8b56-4eaf-8236-c12bf7904b40) and 96 BackgroundJobPerformers started successfully org.jobrunr.server.ServerZooKeeper : Server eddcedcf-8b56-4eaf-8236-c12bf7904b40 is master (this BackgroundJobServer)
8. Панель управления JobRunr
JobRunr поставляется со встроенной панелью мониторинга для отслеживания деталей наших заданий, таких как количество запланированных, поставленных в очередь, обработанных, успешных и неудачных заданий. По умолчанию панель мониторинга выполнения заданий запускается с http://localhost:8000 .
Ниже приведен пример панели мониторинга JobRunr по умолчанию.
Попробуйте запустить задание, и панель мониторинга выполнения задания автоматически отобразит статус задания.
$ curl http://localhost:8080/run-job?name="mkyong" Job is enqueued.
8. Весенний тест и работа
Мы можем объявить spring-boot-starter-test и ожидание для тестирования на конечных точках.
org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.awaitility awaitility test
package com.mkyong;
import org.jobrunr.jobs.states.StateName;
import org.jobrunr.storage.StorageProvider;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
@SpringBootTest(webEnvironment = DEFINED_PORT)
public class JobEndpointTest {
@Autowired
TestRestTemplate restTemplate;
@Autowired
StorageProvider storageProvider;
@Test
@DisplayName("Test job enqueued.")
public void givenEndpoint_whenJobEnqueued_thenJobIsProcessedWithin30Seconds() {
String response = runJobViaRest("from-test");
assertEquals("Job is enqueued.", response);
await()
.atMost(30, TimeUnit.SECONDS)
.until(() -> storageProvider.countJobs(StateName.SUCCEEDED) == 1);
}
@Test
@DisplayName("Test job scheduled.")
public void givenEndpoint_whenJobScheduled_thenJobIsScheduled() {
String response = scheduleJobViaRest("from-test", Duration.ofHours(3));
assertEquals("Job is scheduled.", response);
await()
.atMost(30, TimeUnit.SECONDS)
.until(() -> storageProvider.countJobs(StateName.SCHEDULED) == 1);
}
private String runJobViaRest(String input) {
return restTemplate.getForObject(
"http://localhost:8080/run-job?name=" + input,
String.class);
}
private String scheduleJobViaRest(String input, Duration duration) {
return restTemplate.getForObject(
"http://localhost:8080/schedule-job?name=" + input
+ "&when=" + duration.toString(),
String.class);
}
}
Скачать Исходный Код
$клон git https://github.com/mkyong/spring-boot.git
$cd пружинная загрузка-jobrunr
$mvn пружинная загрузка: бежать
Рекомендации
- ДжоБрун
- Документация JobRunr
- Пружинный ботинок + JUnit 5
- Википедия – ISO 8601
- Ожидание – Асинхронное тестирование
Оригинал: “https://mkyong.com/spring-boot/spring-boot-jobrunr-examples/”