定时任务发展史(二)

原创 2017年07月07日 12:04:24

第一代定时任务系统上线用了大概半年之后,就被我们厌倦了。于是就规划了第二代定时任务系统。

第二代定时任务系统

第二代调度系统主要解决的是,避免每次修改定时任务的执行时间都需要重新启动整个项目。另外也可支持单独重新调度单个定时任务。

我们做了一个请求入口,当更新了库表里面的数据之后,重新请求一下特定的url就会自动重新加载定时任务。

使用scheduler删除定时任务

public void reScheduler() throws Exception {
    // 取消现有的任务
    String[] jobNames = quartzUtil.getJobNames();
    if (null != jobNames && jobNames.length > 0) {
        for (String jobName : jobNames) {
            logger.info("----开始移除任务:" + jobName);
            quartzUtil.cancelJob(jobName);
            logger.info("----成功移除任务:" + jobName);
        }
    }
    logger.info("现有任务已全部取消");
    this.initScheduler();
}
public void cancelJob(String jobName) throws Exception {
    scheduler.pauseTrigger(jobName, Scheduler.DEFAULT_GROUP);
    scheduler.unscheduleJob(jobName, Scheduler.DEFAULT_GROUP);
    scheduler.deleteJob(jobName, Scheduler.DEFAULT_GROUP);
}

使用scheduler重新加载所有的定时任务。

job.setCronExpression(taskInfo.getSchedulerRule());
String jobName = taskInfo.getTaskNo() + "Job";
job.getJobDataMap().put(QuartzJob.OBJECT_ID,objectMethod);
job.setJobName(jobName);
logger.info("----开始部署任务:" + jobName);
quartzUtil.scheduleCronJob(job);
logger.info("----成功部署任务:" + jobName);
public void scheduleCronJob(QuartzJobEntity jobEntity) throws Exception {
    JobDetailBean jobDetail = createJobDetail(jobEntity);
    scheduler.addJob(jobDetail, true);
    CronTriggerBean trigger = new CronTriggerBean();
    trigger.setCronExpression(jobEntity.getCronExpression());
    trigger.setJobDetail(jobDetail);
    trigger.setName(jobEntity.getJobName());
    trigger.setJobName(jobDetail.getName());
    scheduler.scheduleJob(trigger);
}

如果只是重新调度某一个定时任务可以触发单独的调用

// 初始化某个加载定时任务
public void initScheduler(TaskEntity taskInfo) throws Exception {
    // 设置任务信息到quartz,并调度任务
    QuartzJobEntity job = new QuartzJobEntity();
    String objectName = taskInfo.getTaskNo()+"Task";
    String objectMethod = "executeTask";
    job.getJobDataMap().put(QuartzJob.OBJECT_NAME,objectName);
    job.getJobDataMap().put(QuartzJob.OBJECT_METHOD,objectMethod);
    // 单线程方式执行任务
    job.setJobClass(QuartzJob.class);
    job.setCronExpression(taskInfo.getSchedulerRule());
    String jobName = taskInfo.getTaskNo() + "Job";
    job.getJobDataMap().put(QuartzJob.OBJECT_ID,objectMethod);
    job.setJobName(jobName);
    logger.info("----开始部署任务:" + jobName);
    quartzUtil.scheduleCronJob(job);
    logger.info("----成功部署任务:" + jobName);
}

这样我们的第二代定时任务系统就完成了,第二代定时任务是在第一代定时任务的基础上改造的,增加了重新调度所有定时任务和单个定时任务。

第二代定时任务系统的缺点是:定时调度和业务代码耦合

第三代定时任务系统

第二代定时任务上线没有多久,我们就意识到有很多的子系统也需要定时任务,比如订单系统需要45分钟不支付的订单失效,监控系统需要定时扫描是否有业务报警,统计系统需要定时去统计一些数据,但是如果我们给每一个子系统都做一个定时任务的话,就不太合理,很分散。

于是计划开发一个统一的定时任务调度中心,负责整个平台中所有的定时任务的调度,另外规划了监控系统,来监控和分析每次定时任务的执行结果和执行时间等信息。为了更好的管理定时任务开发了简单的管理界面。如下:

根据上图可以看出,通过这个管理界面我们可以非常方便的去修改、启动、暂停定时任务。别的系统如果需要定时任务,可以随时在页面去添加,全部界面化操作,不需要重新启动项目等。

点击详情可以清晰的查看定时任务的上次执行情况

定时任务的支持的调度方式分有两种:http和mq,我们一般建议使用mq。

  • http :使用http一般适用于用时特别少的定时任务。或者接收请求之后立刻返回结果,重新启动另外一个线程去执行具体的业务,业务执行完成之后在通过http回调返回执行结果。

  • mq :使用mq的话,调度系统和业务系统的交互就完全异步来执行,调度系统定时触发后,发送MQ消息给业务系统,业务系统接收到消息开始执行业务,执行完毕之后,再发送MQ系统通知调度系统的执行结果。

主要核心代码

初始化加载

public void initScheduler(){
    List<TaskInformationsEntity> taskList = taskInformationsDao.getTaskList();
    Scheduler scheduler = schedulerBean.getScheduler();
    for(TaskInformationsEntity task : taskList){
        try {
            this.scheduler(task, scheduler);
        } catch (Exception e) {
            logger.error("定时:" + task.getTaskNo() + "启动失败");
        }
    }
}

遍历调度

public void scheduler(TaskInformationsEntity task,Scheduler scheduler){
    TriggerKey triggerKey = TriggerKey.triggerKey(task.getTaskNo(), Scheduler.DEFAULT_GROUP);
    JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withDescription(task.getTaskName()).withIdentity(task.getTaskNo(), Scheduler.DEFAULT_GROUP).build();
    jobDetail.getJobDataMap().put("targetObjectId", task.getTaskNo());
    jobDetail.getJobDataMap().put("executorNo", task.getExecutorNo());
    jobDetail.getJobDataMap().put("sendType", task.getSendType());
    jobDetail.getJobDataMap().put("url", task.getUrl());
    jobDetail.getJobDataMap().put("executeParamter", task.getExecuteParamter());
    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getSchedulerRule());
    CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
    try {
        scheduler.scheduleJob(jobDetail, trigger);
        logger.info("task "+task.getTaskNo()+" schedulerRule :"+task.getSchedulerRule()+" reload succeed");
    } catch (Exception e) {
        logger.error("scheduler--异常:",e);
        throw new RuntimeException();
    }
}

添加定时任务

public String addScheduler(String key){
    TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key);
    if(null != entity){
        Scheduler scheduler = schedulerBean.getScheduler();
        try {
            scheduler.deleteJob(new JobKey(key));
            this.scheduler(entity, scheduler);
            entity.setFrozenStatus(TaskStatusEnum.UNFROZEN);
            entity.setUnfrozenTime(DateUtil.getLastModifyTime());
            entity.setLastModifyTime(DateUtil.getLastModifyTime());
            taskInformationsDao.updateById(entity);
            return "任务启动成功";
        } catch (Exception e) {
            logger.info("异常:",e);
            return "任务启动失败";
        }
    }else{
        return "该任务编号不存在";
    }
}

删除定时任务

public String delScheduler(String key){
    TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key);
    if(null != entity && TaskStatusEnum.UNFROZEN == entity.getFrozenStatus()){
        Scheduler scheduler = schedulerBean.getScheduler();
        try {
            scheduler.deleteJob(new JobKey(key));
            entity.setFrozenStatus(TaskStatusEnum.FROZEN);
            entity.setFrozenTime(DateUtil.getLastModifyTime());
            entity.setLastModifyTime(DateUtil.getLastModifyTime());
            taskInformationsDao.updateById(entity);
            return "暂停任务成功";
        } catch (Exception e) {
            logger.error("异常:",e);
            return "暂停任务异常";
        }
    }else{
        return "该任务编号不存在";
    }
}

重新加载定时任务

public String resumeScheduler(String key){
    TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key);
    if(null != entity){
        Scheduler scheduler = schedulerBean.getScheduler();
        try {
            scheduler.deleteJob(new JobKey(key));
            this.scheduler(entity, scheduler);
            return "重启成功";
        } catch (SchedulerException e) {
            logger.info("异常:",e);
            return "重启异常";
        }
    }else{
        return "该任务编号不存在";
    }
}

项目已经开源,详细的代码请在github上面查看。

zx-quartz

其实最后这版定时调度系统,还是有很多的缺陷,http模式没有进行完善,开源的代码中有部分内部依赖的jar还没有去掉。开放出来仅仅做为交流使用,后期有时间的话再去慢慢完善。也欢迎各网友多提提建议,一起加入完善。


喜欢我的文章,请关注我的公众号

版权声明:本文为博主原创,了解最新劲爆内容,请关注公众号。

相关文章推荐

淘宝网的技术发展史(二) ——Oracle替换MySQL

本文是《淘宝网的技术发展史》系列连载文的第二篇,在告别个人网站时代后,淘宝网的技术发展迎来了由Oracle开启的第二个时代。    MySQL撑不住了    淘宝网作为个人网站发展的时间其...

spring@Scheduled注解执行定时任务(二)

一、XML配置 二、还需要配置跑定时任务的电脑,判断启动该项目的IP与配置中的IP是否一致,一致则跑 三、案例 package com.lifang.callce...

spring schedule定时任务(二):配置文件的方式

接着上一篇,这里使用spring配置文件的方式生成spring定时任务。 1、相应的web.xml没有什么变化,因此便不再罗列。同样的,相应的java代码业务逻辑改动也不大,只是在原来的基础...

动态web技术变迁发展史(二) —— CGI

本篇是系列文章中的第二篇,讲述大名鼎鼎的CGI技术。CGI 全称为Common Gateway Interface (通用网关接口),目的是能够让服务器能够方便的调用外部程序。CGI本身是一套协议和规...

java工具(二)----poi操作excel和xml,定时任务

1. excel入门 http://www.yiibai.com/apache_poi/apache_poi_spreadsheets.html 注:Selenium中可以利用Excel实现“参...

Android四大组件之service(二)——用service、scheduleAtFixedRate写一个后台定时执行任务

点击查看原文上篇介绍了service及其生命周期,这里scheduleAtFixedRate介绍java api中可直接查;这里写一个后台定时执行任务的例子,直接上代码:ScheduledThread...

短信消息通知服务-spring定时计划任务quartz(二)

紧接前篇,贴上业务逻辑代码,即短信服务的代码: 一、自己new一个SendMessage的java类 二、写一个constant类 三、所需lib包 四...

Quartz+Spring 分布式定时任务调度(二)- 集群配置

Quartz 分布式任务调度

[译]Java定时任务调度-Quartz文档(二)Quartz API、Job & Scheduler

主要介绍Quartz API,Job和Scheduler的含义,以及Quartz为什么这么设计。

quartz系列(二):动态定时任务管理

title: quartz系列(二):动态定时任务管理在此篇文章中,探讨一下对quartz任务的动态管理。基本思路是创建两张表分别存放Job、Trigger相关信息,项目启动时加载所有任务,在页面可查...
  • ahmuAMu
  • ahmuAMu
  • 2016年01月04日 01:55
  • 842
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:定时任务发展史(二)
举报原因:
原因补充:

(最多只允许输入30个字)