热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Quartz实现JAVA定时任务的动态配置的方法

这篇文章主要介绍了Quartz实现JAVA定时任务的动态配置的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

先说点无关本文的问题,这段时间特别的不爽,可能有些同学也遇到过。其实也可以说是小事一桩,但感觉也是不容忽视的。我刚毕业时的公司,每个人每次提交代码都有着严格的规范,像table和space的缩进都有严格的要求,可以说你不遵守开发规范就相当于线上bug问题,还是比较严重的。现在发现外面的公司真的是没那么重视这个不重要却又特别重要的问题啊,啊啊啊啊啊啊!!!

什么是动态配置定时任务?

回归正题,说下这次主题,动态配置。没接触过定时任务的同学可以先看下此篇:JAVA定时任务实现的几种方式

定时任务实现方式千人千种,不过基础的无外乎 1、JDK 的Timer类 2、Quartz 3、SpringTask  。生产上三种方式我都有使用过。但是使用过程中用的最多的便是xml配置的方式,这种方式最简单,无代码侵入,也比较好理解。

但是却有个致命的缺点,比如你要改某个任务的触发时间,亦或是你要新增一个任务,暂停一个任务。怎么做?

停应用!改XML配置!重新启动!

是不是很致命。最近重新研究了下Quartz的配置,实现了不停机添加、暂停、删除、立即触发任务的方法,在本篇分享出来,其实也不算是完全的研究,在上家公司已经有大佬实现了,这次是也是基于大佬的理解重新实现下。

国际惯例~先看效果图   ps:文末有彩蛋哦~~

管理界面:

效果图:实在不知道该跑什么了,每隔十秒打一段话吧

 

技术实现 maven依赖

使用springboot做框架支持



 org.quartz-scheduler
 quartz
 2.2.1


 org.springframework
 spring-context-support

数据表

CREATE TABLE `sys_task` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `job_name` varchar(255) DEFAULT NULL COMMENT '任务名',
 `description` varchar(255) DEFAULT NULL COMMENT '任务描述',
 `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
 `bean_class` varchar(255) DEFAULT NULL COMMENT '任务执行时调用哪个类的方法 包名+类名',
 `job_status` varchar(255) DEFAULT NULL COMMENT '任务状态',
 `job_group` varchar(255) DEFAULT NULL COMMENT '任务分组',
 `create_user` varchar(64) DEFAULT NULL COMMENT '创建者',
 `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 `update_user` varchar(64) DEFAULT NULL COMMENT '更新者',
 `update_time` datetime DEFAULT NULL COMMENT '更新时间',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

实现步骤

①启动项目,启动task监听

②读取数据库,将开启的任务job和trigger加载到scheduler调度器

③根据任务调度运行job类

④每次运行利用AdaptableJobFactory实例化job类,以便注入要运行的service

听着是不是很简单,但却还是一头雾水,且听我慢慢道来~~

代码逻辑

第一步:启动项目,加载监听

Quartz配置

Springboot的配置方法,常规Spring项目可以在xml中配置

@Configuration
public class QuartzConfigration {

 @Autowired
 private JobFactory jobFactory;

 @Bean
 public SchedulerFactoryBean schedulerFactoryBean() {
  SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
  try {
   schedulerFactoryBean.setOverwriteExistingJobs(true);
   schedulerFactoryBean.setQuartzProperties(quartzProperties());
   schedulerFactoryBean.setJobFactory(jobFactory);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return schedulerFactoryBean;
 }

 // 指定quartz.properties,可在配置文件中配置相关属性
 @Bean
 public Properties quartzProperties() throws IOException {
  PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  propertiesFactoryBean.setLocation(new ClassPathResource("/config/quartz.properties"));
  propertiesFactoryBean.afterPropertiesSet();
  return propertiesFactoryBean.getObject();
 }

 // 创建schedule
 @Bean(name = "scheduler")
 public Scheduler scheduler() {
  return schedulerFactoryBean().getScheduler();
 }
}

监听器

@Component
@Order(value = 1)
public class ScheduleJobInitListener implements CommandLineRunner {

 @Autowired
 TaskService scheduleJobService;

 @Override
 public void run(String... arg0) throws Exception {
  try {
   scheduleJobService.initSchedule();
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}

CommandLineRunner类似Spring框架的ApplicationListener监听器。

官方的解释是:

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or Order @Order annotation.

第二步:读取数据库,加载scheduler调度器

job方法

@Override
 public void initSchedule() throws SchedulerException {
  // 这里获取任务信息数据
  List jobList = taskMapper.list();
  for (TaskDO task : jobList) {
   if (JobStatusEnum.RUNNING.getCode().equals(task.getJobStatus())) {
    quartzManager.addJob(task);
   }
  }
 }

添加任务到Quartz调度器

/**
  * 添加任务*/ 
 @SuppressWarnings("unchecked")
 public void addJob(TaskDO task) {
  try {
   // 创建jobDetail实例,绑定Job实现类
   // 指明job的名称,所在组的名称,以及绑定job类
   Class<&#63; extends Job> jobClass = (Class<&#63; extends Job>) (Class.forName(task.getBeanClass()).newInstance()
     .getClass());
   JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobName(), task.getJobGroup())// 任务名称和组构成任务key
     .build();
   // 定义调度触发规则
   // 使用cornTrigger规则
   Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup())// 触发器key
     .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
     .withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();
   // 把作业和触发器注册到任务调度中
   scheduler.scheduleJob(jobDetail, trigger);
   // 启动
   if (!scheduler.isShutdown()) {
    scheduler.start();
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

Scheduler作为Quartz的核心调度器,有将近50多个API接口,包括任务的添加,暂停,恢复,删除等一系列的API,这里仅介绍一些常用的,想要了解更多可以稍后看下彩蛋部分。

1、start()方法:只有调用start()方法后,Scheduler线程才开始启动触发器trigger,运行job

2、pauseJob(JobKey jobKey) :根据指定的JobDetail key暂停job。

3、resumeJob(JobKey jobKey) :根据指定的key恢复一个job。

4、deleteJob(JobKey jobKey) :删除一个job

5、triggerJob(JobKey jobKey) :触发一个JobDetail(现在执行)。

6、rescheduleJob(TriggerKey triggerKey, Trigger newTrigger):

用给定的键删除触发器,并存储新的触发器,它必须与同一个作业相关联(新触发器必须具有指定的作业名和组)-然而,新触发器不必具有与旧触发器相同的名称。

第三步:根据任务调度运行job类

其实这一步是不需要我们编写的,在我们将正确的JobDetail 和 Trigger 表达式加载到任务调度后,调度器会自动触发任务的执行

第四步:实例化job类,注入要运行的service

工厂类

@Component
public class JobFactory extends AdaptableJobFactory {
 //这个对象Spring会帮我们自动注入进来,也属于Spring技术范畴.
 //为什么需要这个类呢,在我写的这个demo中,大家可以将此类删掉,发现程序也可以正确运行,可是我为什么还是加上呢。
 //大家可以看下我们的任务类,大家可以看到Job对象的实例化过程是在Quartz中进行的,这时候我们将spring的东西注入进来,肯定是行不通的,所以需要这个类
 @Autowired
 private AutowireCapableBeanFactory capableBeanFactory;

 @Override
 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  //调用父类的方法
  Object jobInstance = super.createJobInstance(bundle);
  //进行注入
  capableBeanFactory.autowireBean(jobInstance);
  return jobInstance;
 }
}

任务类

@DisallowConcurrentExecution //作业不并发
@Component
public class HelloWorldJob implements Job{

 @Override
 public void execute(JobExecutionContext arg0) throws JobExecutionException {
  
  System.out.println("欢迎使用yyblog,这是一个定时任务 --小卖铺的老爷爷!"+ DateUtils.fullTime(new Date()));
  
 }

}

好了,大功告成,一个简单的动态配置的定时任务已经完成。是不是so easy,下面我们再来简单实现下其他的几种常用的api吧。

暂停一个job

public void pauseJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.pauseJob(jobKey);
 }

恢复一个job

 public void resumeJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.resumeJob(jobKey);
 }

删除一个job

 public void deleteJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.deleteJob(jobKey);
 }

立即触发job

public void runJobNow(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.triggerJob(jobKey);
 }

更新job表达式

public void updateJobCron(TaskDO task) throws SchedulerException {

  TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());

  CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

  CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());

  trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

  scheduler.rescheduleJob(triggerKey, trigger);
 }

彩蛋部分~

好了,正文部分基本上就这些,不多废话了,本文没有太多的讲解原理,只是简单的应用,水平不够还望大家见谅。

看了上面的效果图是不是还是有种看的见摸不着的感觉,老爷爷这里也做了个线上的实例供大家体验~有兴趣的同学可以亲自试验一下。

实例地址: http://www.laoyeye.net/management/index 账号/密码:test/123456  菜单:系统设置/计划任务

当然了还有最重要的源码和API文档我也不会忘的。

源码地址: https://github.com/allanzhuo/yyblog

Quartz文档地址: https://github.com/allanzhuo/yyblog/tree/master/doc

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 如何在任意浏览器中轻松安装并使用VSCode——Codeserver简易指南
    code-server 是一款强大的工具,允许用户在任何服务器上部署 VSCode,并通过浏览器进行访问和使用。这一解决方案不仅简化了开发环境的搭建过程,还提供了高度灵活的工作方式。用户只需访问 GitHub 上的官方仓库(GitHub-coder/code-server),即可获取详细的安装和配置指南,快速启动并运行 code-server。无论是个人开发者还是团队协作,code-server 都能提供高效、便捷的代码编辑体验。 ... [详细]
  • SSL 错误:目标主机名与备用证书主题名称不匹配
    在使用 `git clone` 命令时,常见的 SSL 错误表现为:无法访问指定的 HTTPS 地址(如 `https://ip_or_domain/xxxx.git`),原因是目标主机名与备用证书主题名称不匹配。这通常是因为服务器的 SSL 证书配置不正确或客户端的证书验证设置有问题。建议检查服务器的 SSL 证书配置,确保其包含正确的主机名,并确认客户端的证书信任库已更新。此外,可以通过临时禁用 SSL 验证来排查问题,但请注意这会降低安全性。 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • REST与RPC:选择哪种API架构风格?
    在探讨REST与RPC这两种API架构风格的选择时,本文首先介绍了RPC(远程过程调用)的概念。RPC允许客户端通过网络调用远程服务器上的函数或方法,从而实现分布式系统的功能调用。相比之下,REST(Representational State Transfer)则基于资源的交互模型,通过HTTP协议进行数据传输和操作。本文将详细分析两种架构风格的特点、适用场景及其优缺点,帮助开发者根据具体需求做出合适的选择。 ... [详细]
  • 本文介绍了如何在 Windows 系统上利用 Docker 构建一个包含 NGINX、PHP、MySQL、Redis 和 Elasticsearch 的集成开发环境。通过详细的步骤说明,帮助开发者快速搭建和配置这一复杂的技术栈,提升开发效率和环境一致性。 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • 在GitHub上克隆vue-element-admin项目时遇到依赖安装错误
    在 GitHub 上克隆 vue-element-admin 项目后,使用 `npm install` 安装依赖时遇到了未知的 Git 错误。具体错误信息为 `npm ERR! code 128`,提示命令执行失败。这可能是由于网络问题、Git 配置不正确或某些依赖包的仓库地址无效导致的。建议检查网络连接、更新 Git 版本并确保所有依赖项的 URL 正确无误。 ... [详细]
  • 本项目在Java Maven框架下,利用POI库实现了Excel数据的高效导入与导出功能。通过优化数据处理流程,提升了数据操作的性能和稳定性。项目已发布至GitHub,当前最新版本为0.0.5。该项目不仅适用于小型应用,也可扩展用于大型企业级系统,提供了灵活的数据管理解决方案。GitHub地址:https://github.com/83945105/holygrail,Maven坐标:`com.github.83945105:holygrail:0.0.5`。 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 【Linux】CentOS 7 远程连接指南:高效安全的远程管理方法
    在 CentOS 7 中实现高效且安全的远程管理,本文详细介绍了如何检查和安装配置 OpenSSH。首先,通过 `yum list installed` 命令检查系统是否已安装 OpenSSH,若未安装,则使用 `yum install openssh-server` 进行安装。随后,配置 SSH 服务以确保其安全性和稳定性,包括修改默认端口、禁用 root 登录等关键步骤。此外,还提供了常见问题的解决方案,帮助用户顺利进行远程连接。 ... [详细]
  • 掌握PHP框架开发与应用的核心知识点:构建高效PHP框架所需的技术与能力综述
    掌握PHP框架开发与应用的核心知识点对于构建高效PHP框架至关重要。本文综述了开发PHP框架所需的关键技术和能力,包括但不限于对PHP语言的深入理解、设计模式的应用、数据库操作、安全性措施以及性能优化等方面。对于初学者而言,熟悉主流框架如Laravel、Symfony等的实际应用场景,有助于更好地理解和掌握自定义框架开发的精髓。 ... [详细]
  • 本文详细解析了神州数码DCRS5980交换机的基础配置流程和技术要点。首先,通过进入配置模式(`enable`),设置主机名(`hostname 5980`),并创建VLAN,逐步介绍了设备的初始设置步骤。此外,还涵盖了端口配置、IP地址分配及安全设置等关键环节,为用户提供了全面的配置指导。 ... [详细]
author-avatar
coco昵称不可用
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有