Quartz

现代应用程序往往需要定期执行一些任务。 在本指南中,您学习如何使用 Quartz extension 在集群中定期执行任务。

This technology is considered preview.

In preview, backward compatibility and presence in the ecosystem is not guaranteed. Specific improvements might require to change configuration or APIs and plans to become stable are under way. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker.

For a full list of possible extension statuses, check our FAQ entry.

如果您只需要运行内存调度程序,请使用 Scheduler extension。

必备条件

要完成本指南,您需要:

  • 10 分钟以内

  • IDE

  • JAVA_HOME 正确配置安装了JDK 1.8+

  • Apache Maven 3.6.2+

  • 你电脑上安装了 Docker 和 Docker Compose

结构

在本指南中, 我们将会提供一个 Rest API tasks 来直观地展示由 Quartz job 每隔10秒运行所创建的任务列表。

成果

我们建议您在下面的章节中遵循指示,一步一步创建应用程序。 但是你也可以直接跳到已完成的例子。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git, 或下载 压缩包

代码在 quartz-quickstart 目录

创建Maven项目

首先,我们需要一个新项目。 通过以下命令创建一个新项目:

mvn io.quarkus:quarkus-maven-plugin:1.3.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=quartz-quickstart \
    -DclassName="org.acme.quartz.TaskResource" \
    -Dpath="/tasks" \
    -Dextensions="quartz, hibernate-orm-panache, flyway, resteasy-jsonb, jdbc-postgresql"
cd quartz-quickstart

它生成了:

  • Maven 项目结构

  • 一个可在 http://localhost:8080 上访问的着陆页面

  • nativejvm 模式的 Dockerfile 样板文件

  • 应用程序配置文件

  • 一个 org.acme.gartz.TaskResource 接口

  • 相关的测试

Maven项目还引入了 Quarkus Quartz extension

创建任务实体

org.acme.quartz 包中,创建 Task 类,内容如下:

package org.acme.quartz;

import javax.persistence.Entity;
import java.time.Instant;
import javax.persistence.Table;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
@Table(name="TASKS")
public class Task extends PanacheEntity { (1)
    public Instant createdAt;

    public Task() {
        createdAt = Instant.now();
    }

    public Task(Instant time) {
        this.createdAt = time;
    }
}
  1. 使用 Panache 声明实体

创建调度的任务

org.acme.gartz 包中,创建 TaskBean 类,内容如下:

package org.acme.quartz;

import javax.enterprise.context.ApplicationScoped;

import javax.transaction.Transactional;

import io.quarkus.scheduler.Scheduled;

@ApplicationScoped (1)
public class TaskBean {

    @Transactional
    @Scheduled(every = "10s") (2)
    void schedule() {
        Task task = new Task(); (3)
        task.persist(); (4)
    }
}
  1. application scope 中声明 bean。

  2. 使用 @Scheduled 注解指示 Quarkus 每10 秒运行此方法。

  3. 以当前开始时间创建一个新的 Task

  4. 使用 Panache 在数据库中保存 task。

编程方式调度 Job

还可以直接利用 Quartz API。 您可以将底部的 org.quartz.Scheduler 注入到任何 bean 中:

package org.acme.quartz;

@ApplicationScoped
public class TaskBean {

    @Inject
    org.quartz.Scheduler quartz; (1)

    void onStart(@Observes StartupEvent event) {
       JobDetail job = JobBuilder.newJob(MyJob.class)
                         .withIdentity("myJob", "myGroup")
                         .build();
       Trigger trigger = TriggerBuilder.newTrigger()
                            .withIdentity("myTrigger", "myGroup")
                            .startNow()
                            .withSchedule(
                               SimpleScheduleBuilder.simpleSchedule()
                                  .withIntervalInSeconds(10)
                                  .repeatForever())
                            .build();
       quartz.scheduleJob(job, trigger); (2)
    }

    @Transactional
    void performTask() {
        Task task = new Task();
        task.persist();
    }

    // A new instance of MyJob is created by Quartz for every job execution
    static class MyJob implements Job {

       public void execute(JobExecutionContext context) throws JobExecutionException {
          Arc.container().instance(TaskBean.class).get(). performTask(); (3)
       }

    }
}
  1. 注入底层的 org.quartz.Scheduler 实例。

  2. 使用 Quartz API 安排一个新 job。

  3. 查找 TaskBea 的 bean 实例,然后从 job 中调用 performTask() 方法。

默认情况下,调度程序不会开始,除非找到一个 @Scheduled 业务方法。 对于"纯"编程调度您可能需要强制启动调度程序。 另见 Quartz 配置参考.

更新应用程序配置文件

编辑 application.properties 文件,并添加下面的配置:

# Quartz configuration
quarkus.quartz.clustered=true (1)
quarkus.quartz.store-type=db (2)

# Datasource configuration.
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test

# Hibernate configuration
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=no-file

# flyway configuration
quarkus.flyway.connect-retries=10
quarkus.flyway.table=flyway_quarkus_history
quarkus.flyway.migrate-at-start=true
quarkus.flyway.baseline-on-migrate=true
quarkus.flyway.baseline-version=1.0
quarkus.flyway.baseline-description=Quartz
  1. 指示调度程序将以集群模式运行

  2. 使用数据库存储来保存与作业相关的信息,以便它们可以在节点之间共享

更新接口和测试

编辑 TaskResource 类,更新成:

package org.acme.quartz;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/tasks")
@Produces(MediaType.APPLICATION_JSON)
public class TaskResource {

    @GET
    public List<Task> listAll() {
        return Task.listAll(); (1)
    }
}
  1. 从数据库中获取创建的任务列表

我们还需要更新测试代码。 编辑 TaskResourceTest 成:

package org.acme.quartz;

import io.quarkus.test.junit.QuarkusTest;

import static org.hamcrest.Matchers.greaterThanOrEqualTo;

import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class TaskResourceTest {

    @Test
    public void tasks() throws InterruptedException {
        Thread.sleep(1000); // wait at least a second to have the first task created
        given()
                .when().get("/tasks")
                .then()
                .statusCode(200)
                .body("size()", is(greaterThanOrEqualTo(1))); (1)
    }
}
  1. 确保我们有一个 200 响应并至少一个任务已创建

创建 Quartz 的表

添加 SQL 迁移文件 src/main/resources/db/migration/V2.0.0QuarkusQuartzTasks.sql ,文件内容从 https://github.com/quarkusio/quarkus-quickstarts/blob/master/quartz-quickstart/src/main/resources/db/migration/V2.0.0QuarkusQuartzTasks.sql[V2.0.0__QuarkusQuartzTasks.sql] 复制.

配置负载均衡

在根目录中,创建一个包含以下内容的 nginx.conf 文件:

user  nginx;

events {
    worker_connections   1000;
}

http {
        server {
              listen 8080;
              location / {
                proxy_pass http://tasks:8080; (1)
              }
        }
}
  1. 将所有流量路由到我们的任务应用程序

部署应用程序设置

在根目录中,创建一个包含以下内容的 docker-compose.yml 文件:

version: '3'

services:
  tasks: (1)
    image: quarkus-quickstarts/quartz:1.0
    build:
      context: ./
      dockerfile: src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
    environment:
      QUARKUS_DATASOURCE_URL: jdbc:postgresql://postgres/quarkus_test
    networks:
      - tasks-network
    depends_on:
      - postgres

  nginx: (2)
    image: nginx:1.17.6
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - tasks
    ports:
      - 8080:8080
    networks:
      - tasks-network

  postgres: (3)
    image: postgres:11.3
    container_name: quarkus_test
    environment:
      - POSTGRES_USER=quarkus_test
      - POSTGRES_PASSWORD=quarkus_test
      - POSTGRES_DB=quarkus_test
    ports:
      - 5432:5432
    networks:
      - tasks-network

networks:
  tasks-network:
    driver: bridge
  1. 定义 task service

  2. 定义 nginx 负载均衡器将传入流量导入到适当节点

  3. 定义要运行数据库的配置

运行数据库

在一个单独的终端中,运行下面的命令:

docker-compose up postgres (1)
  1. 使用 docker-compose.yml 文件中提供的配置选项启动数据库实例

在 Dev 模式下运行应用程序

运行应用程序: ./mvnw quarkus:dev 。 几秒钟后,打开另一个终端并运行 curl localhost:8080/tasks 来验证我们至少已创建了一个任务。

像往常一样,应用程序可以使用 ./mvnw cound package 进行打包并用 -runner.jar 文件执行。 您也可以使用 ./mvnw clean package -Pnative 生成 native 可执行文件。

打包应用程序并运行几个实例

应用程序可以使用 ./mvnw clean package 打包。 构建成功后,运行下面的命令:

docker-compose up --scale tasks=2 --scale nginx=1 (1)
  1. 启动两个应用程序实例和一个负载平衡器

几秒钟后,在另一个终端中 运行 curl localhost:8080/tasks 以验证任务是在不同的时间和间隔10秒创建的。

您也可以使用 ./mvnw clean package -Pnative 生成 native 可执行文件。

Quartz 配置参考

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

Enable cluster mode or not. If enabled make sure to set the appropriate cluster properties.

boolean

false

The type of store to use. When using the db store type configuration value make sure that you have the datasource configured. See Configuring your datasource for more information. To create Quartz tables, you can perform a schema migration via the Flyway extension using a SQL script matching your database picked from Quartz repository.

ram, db

ram

The name of the datasource to use. Optionally needed when using the db store type. If not specified, defaults to using the default datasource.

string

The size of scheduler thread pool. This will initialize the number of worker threads in the pool.

int

25

Thread priority of worker threads in the pool.

int

5

By default, the scheduler is not started unless a io.quarkus.scheduler.Scheduled business method is found. If set to true the scheduler will be started even if no scheduled business methods are found. This is necessary for "pure" programmatic scheduling.

boolean

false

quarkus.pro 是基于 quarkus.io 的非官方中文翻译站 ,最后更新 2020/04 。
沪ICP备19006215号-8
QQ交流群:1055930959
微信群: