热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Quartz实现数据库动态配置定时任务

为什么80%的码农都做不了架构师?项目实战或许实现的方式跟之前的代码有点不一样的1.定时任务的配置信息ConfigurationpublicclassSche

为什么80%的码农都做不了架构师?>>>   hot3.png

项目实战

或许实现的方式跟之前的代码有点不一样的

1.定时任务的配置信息

@Configuration
public class ScheduleConfigration {@Autowiredprivate ScheduleInfoAction scheduleInfoAction;@Autowiredprivate ChannelSyncTask ChannelSyncTask;/*** 用于5分钟轮训检查cron修改(基本不需要修改)* @return*/@Bean(name = "jobDetail")public MethodInvokingJobDetailFactoryBean jobDetail(){MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();methodInvokingJobDetailFactoryBean.setConcurrent(false);methodInvokingJobDetailFactoryBean.setTargetObject(scheduleInfoAction);methodInvokingJobDetailFactoryBean.setTargetMethod("reScheduleJob");return methodInvokingJobDetailFactoryBean;}/*** 用于5分钟轮训检查cron修改(基本不需要修改)* @return*/@Bean(name = "cronTrigger")public CronTriggerFactoryBean cronTrigger(){CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();cronTriggerFactoryBean.setJobDetail(jobDetail().getObject());// 设置默认刷新croncronTriggerFactoryBean.setCronExpression(Properties.getString("refresh.default.cron"));return cronTriggerFactoryBean;}/*** dycChannel 任务,需要添加新的定时任务,需要重复配置JobDetail,CronTrigger* @return*/@Bean(name = "channelSyncJobCronJobDetail")public MethodInvokingJobDetailFactoryBean channelSyncJobCronJobDetail(){MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();methodInvokingJobDetailFactoryBean.setConcurrent(false);methodInvokingJobDetailFactoryBean.setTargetObject(channelSyncTask);methodInvokingJobDetailFactoryBean.setTargetMethod("doTask");return methodInvokingJobDetailFactoryBean;}@Bean(name = "channelSyncJobCronTrigger")public CronTriggerFactoryBean channelSyncJobCronTrigger(){CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();cronTriggerFactoryBean.setJobDetail(channelSyncJobCronJobDetail().getObject());cronTriggerFactoryBean.setCronExpression("0 0 1 * * ?");return cronTriggerFactoryBean;}/*** repeat code JobDetail and CronTrigger ...**//*** 调度工厂* @return*/@Bean(name = "schedulerFactory")public SchedulerFactoryBean schedulerFactoryBean(){SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();//在添加新的触发器时,增加相应的触发器schedulerFactoryBean.setTriggers(cronTrigger().getObject(),channelSyncJobCronTrigger().getObject());return schedulerFactoryBean;}
}

2.刷新的定时任务

@Component
public class ScheduleInfoAction{private Logger logger = LoggerFactory.getLogger(ScheduleInfoAction.class);private Scheduler scheduler;public void reScheduleJob() throws SchedulerException {scheduler = (Scheduler) SpringContextUtils.getBean("schedulerFactory");// 数据库取任务列表List jobs = yunyingDao.getJobCodesFromDB(null);if (jobs != null && !jobs.isEmpty()) {for (String job : jobs) {TriggerKey triggerKey = new TriggerKey(job + "CronTrigger", Scheduler.DEFAULT_GROUP);CronTriggerImpl trigger = (CronTriggerImpl) scheduler.getTrigger(triggerKey);String dbCronExpression = getCronExpressionFromDB(job);String originConExpression;if (StringUtils.isNotBlank(dbCronExpression)){if (trigger != null){originConExpression = trigger.getCronExpression();if(!dbCronExpression.equalsIgnoreCase(originConExpression)){try{trigger.setCronExpression(dbCronExpression);scheduler.rescheduleJob(triggerKey, trigger);logger.info("jobCode:{}, dbCron:{}, originCron:{}",job,dbCronExpression,originConExpression);} catch (Exception e) {logger.error("jobCode:{}, dbCron:{}, originCron:{}, refresh Cron Error:{}" , job,dbCronExpression, originConExpression, e);}}}else {trigger = (CronTriggerImpl)SpringContextUtils.getBean(job + "CronTrigger");if (trigger != null){scheduler.scheduleJob(trigger);logger.info("scheduleJob:{}", job);}}}else {if (trigger != null){scheduler.unscheduleJob(triggerKey);logger.info("unscheduleJob:{}", job);}}}}}// 数据库取最新的Cron值private String getCronExpressionFromDB(String jobName){return yunyingDao.getCronByCode(jobName);}
}

3.设置定时任务

    1)基础抽象类的实现,(由于可能处于分布式环境中,需要使用zookeeper的分布式锁)

public abstract class BaseTask {private Logger logger = LoggerFactory.getLogger(BaseTask.class);public abstract void execute();public void doTask(){String clazzName = this.getClass().getSimpleName();String className = clazzName.substring(0,1).toLowerCase() + clazzName.substring(1);String lockPath = "/oper/schedule/" + className + "/lock";ZooKeeperClient zkClient = (ZooKeeperClient)SpringContextUtils.getBean("zkClient");CronTriggerImpl cronTrigger = (CronTriggerImpl)SpringContextUtils.getBean(className.replace("Task","Job") + "CronTrigger");logger.info("lock path:{},zkClient:{},trigger cron:{}", lockPath, zkClient, cronTrigger.getCronExpression());Boolean isGetLocker = false;InterProcessMutex lock = new InterProcessMutex(zkClient.getClient(), lockPath);try {if (lock.acquire(2000, TimeUnit.MILLISECONDS)){isGetLocker = true;execute();}} catch (Exception e) {logger.error("BaseTask 执行锁任务时抛错:", e);}finally {try {if(isGetLocker){lock.release();}} catch (Exception e) {logger.error("释放锁出错",e);}}}
}

需要执行的定时任务的代码实现继承BaseTask,并实现业务代码。

不过,这里我们中间又使用了Groovy代码,由于Groovy代码在这里可以直接对数据库进行操作,所以这里继承BaseTask 的定时任务,我们是调用Groovy Class 的方法。

    2)定时任务的实现

@Component
public class DycChannelSyncTask extends BaseTask{private Logger logger = LoggerFactory.getLogger(DycChannelSyncTask.class);public void execute() {logger.info("渠道信息开始执行任务:" + TimeUtils.formatTime(new Date(), TimeUtils.FORMAT_YYYYMMDDHHMMSS));//进行数据抽取存储,这里调的是groovy的方法channelSyncJob.doJob();logger.info("渠道信息任务执行完成:" + TimeUtils.formatTime(new Date(), TimeUtils.FORMAT_YYYYMMDDHHMMSS));}
}

4.需要引入的Maven

org.quartz-schedulerquartz2.2.1org.codehaus.groovygroovy-all2.4.6mysqlmysql-connector-java5.1.31org.apache.zookeeperzookeeperorg.slf4jslf4j-log4j12log4jlog4jorg.apache.curatorcurator-frameworkorg.apache.curatorcurator-recipes

5.数据库中定时任务的表结构

CREATE TABLE `tb_job_config` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',`jobCode` varchar(32) DEFAULT NULL COMMENT '商户',`jobName` varchar(32) DEFAULT NULL COMMENT '渠道编号',`cron` varchar(128) DEFAULT NULL,`jobDetail` longtext COMMENT '渠道名称',`status` smallint(1) DEFAULT NULL COMMENT '1、有效 2 无效',PRIMARY KEY (`id`),UNIQUE KEY `merchantNo_channelCode_unique` (`jobCode`,`jobName`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=164 DEFAULT CHARSET=utf8 COMMENT='任务调度配置表';
INSERT INTO `tb_job_config` (`id`, `jobCode`, `jobName`, `cron`, `jobDetail`, `status`)
VALUES ('139', 'channelUserSyncJob', '渠道用户同步', '0 0 0/1 * * ? ', '{\"intervalHours\":\"2\"}', '1');// 定时任务执行完,记录一下任务执行情况,有助于排查问题等等
CREATE TABLE `tb_job_execute_log` (`id` bigint(64) NOT NULL AUTO_INCREMENT,`jobCode` varchar(255) DEFAULT NULL COMMENT '任务编号',`totalNum` bigint(64) DEFAULT NULL COMMENT '记录总数',`successNum` bigint(64) DEFAULT NULL COMMENT '记录成功数',`errorNum` bigint(64) DEFAULT NULL COMMENT '记录错误数',`recordTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间',PRIMARY KEY (`id`),KEY `Index_jobCode` (`jobCode`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=52810 DEFAULT CHARSET=utf8 COMMENT='任务执行情况记录表';

另外注意的是数据库中命名的Job,要与实际代码中的命名规则保持一致。

我觉得使用之前的Job,获取我们这边的代码可以简化很多。


转:https://my.oschina.net/itommy/blog/974039



推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有