项目实战

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

1.定时任务的配置信息

@Configuration
public class ScheduleConfigration { @Autowired
private ScheduleInfoAction scheduleInfoAction; @Autowired
private 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());
// 设置默认刷新cron
cronTriggerFactoryBean.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<String> 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

		<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency> <!-- groovy要用到的,数据库连接,zookeeper要用到的(我这的是封装好的) -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.6</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency> <!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>

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,获取我们这边的代码可以简化很多。

Quartz实现数据库动态配置定时任务的更多相关文章

  1. 基于Django+celery二次开发动态配置定时任务 ( 二)

    一.需求 结合上一篇,使用djcelery模块开发定时任务时,定时任务的参数都保存在djcelery_periodictask表的args.kwargs字段里,并且是json格式.那么,当定时任务多了 ...

  2. Django + Celery 实现动态配置定时任务

    哈喽,今天给大家分享一篇Django+Celery实现动态配置定时任务,因为最近也是无意间看到一位大佬关于这块的文章,然后自己觉得不错,也想学习写一下,然后最终实现功能是在前端页面统一管理计划任务,大 ...

  3. celery 动态配置定时任务

    How to dynamically add or remove tasks to celerybeat? · Issue #3493 · celery/celery https://github.c ...

  4. springboot整合Quartz实现动态配置定时任务

    前言 在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功能. 一.新建一个springboot工程,并添加依赖 & ...

  5. 基于Django+celery二次开发动态配置定时任务 ( 一 )

    需求: 前端时间由于开发新上线一大批系统,上完之后没有配套的报表系统.监控,于是乎开发.测试.产品.运营.业务部.财务等等各个部门就跟那饥渴的饿狼一样需要 各种各样的系统数据满足他们.刚开始一天一个还 ...

  6. spring boot1.0 集成quartz 动态配置定时任务

    转载自 https://www.imooc.com/article/36278 一.Quartz简介了解 Quartz Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应 ...

  7. Quartz实现分布式可动态配置的定时任务

    关键词: 1. 定时任务 2. 分布式 3. 可动态配置触发时间 一般通过Quartz实现定时任务很简单.如果实现分布式定时任务需要结合分布式框架选择master节点触发也可以实现.但我们有个实际需求 ...

  8. Quartz实现JAVA定时任务的动态配置

    什么是动态配置定时任务? 首先说下这次主题,动态配置.没接触过定时任务的同学可以先看下此篇:JAVA定时任务实现的几种方式 定时任务实现方式千人千种,不过基础的无外乎 1.JDK 的Timer类 2. ...

  9. Spring+Quartz配置定时任务

    一.Quartz介绍 在企业应用中,我们经常会碰到时间任务调度的需求,比如每天凌晨生成前天报表,每小时生成一次汇总数据等等.Quartz是出了名的任务调度框架,它可以与J2SE和J2EE应用程序相结合 ...

随机推荐

  1. cat 合并文件或查看文件内容

    1.命令功能 cat 合并文件或者查看文件内容. 2.语法格式 cat   option    file 参数说明 参数 参数说明 -n 打印文本,并显示每行行号并且空白行也同样包括 -b 与-n用法 ...

  2. CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.ustc.edu.cn/anaconda/pkg

    conda安装时一直报错,换源什么的都不好使,折腾了半天,直到看到https://blog.csdn.net/u013383596/article/details/87718472 将https改为h ...

  3. tensorflow函数介绍(4)

    1.队列的实现: import tensorflow as tf q=tf.FIFOQueue(2,'int32') #创建一个先进先出队列,指定队列中最多可以保存两个元素,并指定类型为整数. #先进 ...

  4. c++11 指针空值

    1. 引入nullptr的必要性: 典型的指针初始化是将其指向一个空的位置.比如: int* my_ptr = 0; int* my_ptr = NULL; 一般情况下,NULL是一个宏定义. #un ...

  5. linux-Centos 7下bond与vlan技术的结合[推荐]

    https://blog.51cto.com/sf1314/2073519 服务器eth0与eth1作bonding,捆绑成bond0接口,服务器对端交换机端口,同属于100.101号vlan接口 v ...

  6. visual studio 编译报错:未能向文件“....csproj.FileListAbsolute.txt”写入命令行,对路径的访问被拒绝

    在网上开始查找出错的解决方法,终于找到了,原来解决方法这么简单,当初以为是权限的问题,后来发现不是权限问题,在VSS中比以前多了两个目录“bin”和“obj”,可能是有人上传的时候将这两个文件夹一起上 ...

  7. python全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  8. 2、投资之基金 - IT人思维之投资

    笔者曾经对基金进行投资,但是当时对基金不是很了解,只是为了投资而去投资.现在,笔者对基金的投资有了更深入的了解认识,所以就有了本文. 基金投资在国内还是挺流行的,虽然其是从国外引进来的概念经验,但是国 ...

  9. 杂项 List

    题目 1. 栈    #A 表达式的转换 (Unaccepted) 2. STL 模板库 #B 双栈排序(Unaccepted)    #C 垃圾陷阱(Accepted)    #D 合并果子(Acc ...

  10. wangjunkai

    <!Doctype html><html lang="en"> <head> <meta http-equiv="Content ...