springboot+quartz以持久化的方式实现定时任务

@SpringBootApplication
@EnableScheduling
public class Application {

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}
// cron为每秒执行一次
@Scheduled(cron = "* * * * * ?")
public void print(){
    System.out.println("执行定时任务");
}

}
结果
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
简单的定时任务就可以用这种方式来做,cron表达式的结果为任务执行的间隔时间。这里放几个在线生成cron表达式的链接西施-王昭君-貂蝉-杨玉环四大美女!香不香

然而
实际开发中,我们的任务可能有很多,且需要手动操作单个/全部的任务,比如添加、开启、停止、继续等等操作。那么伴随着(千牛B类。。。)的BGM有请quartz登场。

quartz
整合

org.springframework.boot
spring-boot-starter-quartz

quartz的三要素
调度器Scheduler
启动触发器去执行任务

触发器Trigger
用来定义Job(任务)触发条件、触发时间,触发间隔,终止时间等

任务job
具体要执行的任务内容

使用
使用quartz是需要配置文件的,quartz.properties
在quartz的jar包的org.quartz包下可以找到默认的配置文件quartz.properties

Default Properties file for use by StdSchedulerFactory

to create a Quartz Scheduler Instance, if a different

properties file is not explicitly specified.

名字

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

实例化ThreadPool时,使用的线程类为SimpleThreadPool

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool

线程总个数

org.quartz.threadPool.threadCount: 10

线程的优先级

org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

持久化方式,默认持久化在内存中,后面我们使用db的方式

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
quartz任务持久化到db则需要一些官方定义的数据库表,表的sql文件可以在quartz的jar包里找到

坐标org.quartz.impl.jdbcjobstore,可以看到里面有很多sql文件,有各种数据库的,咱们用MySQL的,咱们不需要手动执行sql语句,后面咱们在启动项目的时候自动初始化。

创建我们自己的properties文件

实例化ThreadPool时,使用的线程类为SimpleThreadPool

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

threadCount和threadPriority将以setter的形式注入ThreadPool实例

并发个数

org.quartz.threadPool.threadCount=10

优先级

org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.jobStore.misfireThreshold=5000
#持久化使用的类
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库中表的前缀
org.quartz.jobStore.tablePrefix=QRTZ_
#数据源命名
org.quartz.jobStore.dataSource=qzDS
#qzDS 数据源,我们使用hikaricp,默认的是c3p0
org.quartz.dataSource.qzDS.provider=hikaricp
org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
org.quartz.dataSource.qzDS.user=root
org.quartz.dataSource.qzDS.password=123456
org.quartz.dataSource.qzDS.maxConnections=10
既然我们没有使用默认的连接池,那么就探索一下,上源码!
在这个包下:org.quartz.utils,有一个PoolingConnectionProvider,顾名思义,连接池提供者
部分源码

public interface PoolingConnectionProvider extends ConnectionProvider {

/** The pooling provider. */
String POOLING_PROVIDER = "provider";

/** The c3p0 pooling provider. */
String POOLING_PROVIDER_C3P0 = "c3p0";

/** The Hikari pooling provider. */
String POOLING_PROVIDER_HIKARICP = "hikaricp";

}
然后HikariCpPoolingConnectionProvider这个类实现了PoolingConnectionProvider,自行查看。
我们可以在org.quartz.impl下的StdSchedulerFactory中搜索c3p0找到

if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) {
cpClass = “org.quartz.utils.HikariCpPoolingConnectionProvider”;
}
else {
cpClass = “org.quartz.utils.C3p0PoolingConnectionProvider”;
}
剩下的自己多看看吧,起始源码研究起来没有想象中那么难那么乏味(我也不喜欢看源码),但是这个源码看起来确实小有成就感。

回到正题频道,配置application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/quartz?characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2B8
username: root
quartz:
jdbc:
initialize-schema: always
job-store-type: jdbc
initialize-schema: always每次启动项目,总是初始化数据库表自动创建表的关键地方,流程是先删除数据库表,再创建,如果表不存在,则抛异常,但是不会影响后面的生成表,下次再启动项目的时候,由于表已经存在了,所以不会再抛异常了
job-store-type: jdbc就是任务持久化类型,我们用jdbc

我们可能要在job里注入spring对象,不做配置,是无法注入的。
/**

  • @author: taoym

  • @date: 2020/6/4 11:32

  • @desc: 一定要自定义JobFactory重写SpringBeanJobFactory的createJobInstance方法,否则在job中是获取不到spring容器中的bean的
    */
    @Component
    public class JobFactory extends SpringBeanJobFactory {

    @Autowired
    private AutowireCapableBeanFactory beanFactory;

    /**

    • 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire
      */
      @Override
      protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
      Object jobInstance = super.createJobInstance(bundle);
      beanFactory.autowireBean(jobInstance);
      return jobInstance;
      }
      }
      创建quartz的配置文件
      @Configuration
      public class QuartzConfig {

    @Autowired
    private JobFactory jobFactory;

    /**

    • 读取quartz.properties 文件
    • 将值初始化
    • @return
      */
      @Bean
      public Properties quartzProperties() throws IOException {
      PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
      propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
      propertiesFactoryBean.afterPropertiesSet();
      return propertiesFactoryBean.getObject();
      }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    schedulerFactoryBean.setJobFactory(jobFactory);
    schedulerFactoryBean.setQuartzProperties(quartzProperties());
    return schedulerFactoryBean;
    }

    /**

    • 初始化监听器
    • @return
      */
      @Bean
      public QuartzInitializerListener executorListener() {
      return new QuartzInitializerListener();
      }

    @Bean(name = “scheduler”)
    public Scheduler scheduler() throws IOException {
    return schedulerFactoryBean().getScheduler();
    }
    }
    创建触发器组件
    public class TriggerComponent {

    /**

    • @author: taoym
    • @date: 2020/6/1 10:35
    • @desc: 构建cron触发器
      */
      public static Trigger cronTrigger(String cron) {
      CronTrigger cronTrigger = TriggerBuilder.newTrigger()
      .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing())
      .build();
      return cronTrigger;
      }

    public static Trigger cronTrigger(String cron, JobDataMap jobDataMap) {
    CronTrigger cronTrigger = TriggerBuilder.newTrigger()
    .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing())
    .usingJobData(jobDataMap)
    .build();
    return cronTrigger;
    }
    }
    触发器就用这个组件来获取就行了。

创建任务
@DisallowConcurrentExecution
public class TestJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {

}

}
jobExecutionContext这里面可以获取任务组、任务名、触发器组、触发器名、jobdetail等信息。
那个注解是为了让同一个实例(jobdetail)只能单线程执行。
可以这么理解,job为接口,jobdetail为实现类,a是其中一个实现类,a需要花费100s执行一定的操作,而你给的定时器是没50s就执行一次操作,a在执行到一半的时候又需要开启一个线程来执行。使用了DisallowConcurrentExecution就相当于a没有把操作执行完的时候,a不允许开启线程再执行当前操作。
不知道我的描述是否易懂!

按需创建自己的任务表,我是用定时任务做爬虫的(小爬虫)
CREATE TABLE quartz_job (
id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘编号’,
job_name varchar(50) DEFAULT ‘’ COMMENT ‘任务名’,
job_group varchar(50) DEFAULT ‘’ COMMENT ‘任务组名称’,
job_desc varchar(255) DEFAULT ‘’ COMMENT ‘job描述’,
cron varchar(50) DEFAULT ‘’ COMMENT ‘cron表达式’,
status tinyint(1) DEFAULT ‘0’ COMMENT ‘状态’,
url varchar(255) DEFAULT ‘’ COMMENT ‘请求地址’,
param varchar(255) DEFAULT ‘’ COMMENT ‘参数’,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
我们添加任务的时候不和quartz打交道,把任务放到数据库即可。别慌,后面有用到他的地方。
这个表需要有增删改查操作,我们会在系统中查询任务列表选择单个或者所有任务开始执行

执行任务
@Resource
private QuartzJobMapper quartzJobMapper;
@Autowired
private Scheduler scheduler;

@Override
public String start(Integer id) {

	JobDataMap jobDataMap = new JobDataMap();
    jobDataMap.put(k,v);
    
    QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(id);
    
    JobKey jobKey = JobKey.jobKey(quartzJob.getJobName(), quartzJob.getJobGroup());
    
    jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).storeDurably().build();
    
    Trigger trigger = TriggerComponent.cronTrigger(quartzJob.getCron(), jobDataMap);
    try {
        scheduler.scheduleJob(jobDetail, trigger);
        quartzJobMapper.updateStatus(true, id);
        return "开始任务执行成功";
    } catch (SchedulerException se) {
        log.info("开始任务的时候发生了错误");
    }
    return "开始任务的时候发生了错误,请检查日志";
}
深圳网站建设www.sz886.com
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在 Spring Boot 中集成 Quartz。可以通过添加以下依赖来实现: ```xml <!-- Quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> ``` 然后,在 Spring Boot 中配置 Quartz。可以创建一个 `SchedulerFactoryBean` 实例,并设置相关属性,例如线程池大小、任务调度器等等。可以在 `application.properties` 文件中设置相关属性: ```properties # Quartz Scheduler spring.quartz.job-store-type=jdbc spring.quartz.jdbc.initialize-schema=always spring.quartz.properties.org.quartz.scheduler.instanceName=QuartzScheduler spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate spring.quartz.properties.org.quartz.jobStore.useProperties=true spring.quartz.properties.org.quartz.jobStore.dataSource=myDataSource spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_ spring.quartz.properties.org.quartz.dataSource.myDataSource.driverClassName=com.mysql.jdbc.Driver spring.quartz.properties.org.quartz.dataSource.myDataSource.URL=jdbc:mysql://localhost:3306/quartz spring.quartz.properties.org.quartz.dataSource.myDataSource.user=root spring.quartz.properties.org.quartz.dataSource.myDataSource.password=root spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool spring.quartz.properties.org.quartz.threadPool.threadCount=10 spring.quartz.properties.org.quartz.threadPool.threadPriority=5 spring.quartz.properties.org.quartz.jobStore.isClustered=true spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=20000 spring.quartz.properties.org.quartz.jobStore.maxMisfiresToHandleAtATime=1 ``` 然后,需要创建一个 `Job` 类来执行具体的任务。例如: ```java public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 执行具体的任务逻辑 System.out.println("Hello Quartz!"); } } ``` 最后,需要创建一个 `CronTriggerFactoryBean` 实例,并设置相关属性。例如: ```java @Configuration public class QuartzConfig { @Bean public JobDetailFactoryBean myJobDetail() { JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(MyJob.class); factory.setDurability(true); // 任务持久化 return factory; } @Bean public CronTriggerFactoryBean myCronTrigger(@Qualifier("myJobDetail") JobDetail jobDetail) { CronTriggerFactoryBean factory = new CronTriggerFactoryBean(); factory.setJobDetail(jobDetail); factory.setCronExpression("0/5 * * * * ?"); // 每隔 5 秒触发一次 return factory; } @Bean public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("myCronTrigger") CronTrigger cronTrigger) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setTriggers(cronTrigger); return factory; } } ``` 这里的 `myJobDetail` 方法返回一个 `JobDetailFactoryBean` 实例,它指定了具体的任务类。`myCronTrigger` 方法返回一个 `CronTriggerFactoryBean` 实例,它指定了触发器的具体规则。`schedulerFactoryBean` 方法返回一个 `SchedulerFactoryBean` 实例,它将任务和触发器关联起来,并返回一个 `Scheduler` 实例,可以通过它来启动和停止任务调度器。 最后,在 Vue 中实现调用后端接口来启动和停止任务调度器。可以使用 Axios 进行 HTTP 请求。例如: ```javascript import axios from 'axios' // 启动任务调度器 export function startScheduler () { return axios.post('/scheduler/start') } // 停止任务调度器 export function stopScheduler () { return axios.post('/scheduler/stop') } ``` 这里的 `/scheduler/start` 和 `/scheduler/stop` 是后端接口的 URL。可以在后端使用 Spring Boot 的 MVC 模块来实现这两个接口,例如: ```java @RestController @RequestMapping("/scheduler") public class SchedulerController { @Autowired private Scheduler scheduler; @PostMapping("/start") public void start() throws SchedulerException { scheduler.start(); } @PostMapping("/stop") public void stop() throws SchedulerException { scheduler.shutdown(); } } ``` 这里的 `scheduler` 是通过 `SchedulerFactoryBean` 创建的 `Scheduler` 实例,可以通过 `start()` 和 `shutdown()` 方法来启动和停止任务调度器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值