定时任务(Java Scheduled Task & Quartz)

很多的项目文件都需要备份,Linux系统提供了一个crontab可以帮助我们完成这个定时任务
http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html

一、概述

  1. cron 是类 Unix 系统中最为实用的工具之一;
  2. 定时任务(cron job)被用于执行需要周期性执行的命令,利用这个定时任务,可以配置命令或者脚本,让它们在某个设定的时间内周期性地运行;
  3. cron 服务在系统后台运行,即以守护进程的方式运行,并且会持续地检查 /etc/crontab 文件和目录/etc/cron.*/以及 /var/spool/cron/

二、Linux系统中的Cron的一些常见命令

  1. 查看目前的定时任务

    crontab -l
    
  2. 制定定时任务

    • 生成cron表达式:http://cron.qqe2.com/
      • 注意cron只能精确到分钟,有分钟,小时,日,周,月;而且生成的表达式中的?修改成*,否则报错
    • 生成文件
      crontab -e
      
    • 每分钟执行一次的内容,保存后就会生效
      ***** sh <sh-file>
      
    • 查看执行日志
      tail -f /var/log/cron
      

五、Corn表达式的编写

  1. Corn表达式组成:

    <seconds> <minutes> <hours> <day-of-month> <month> <day-of-week> [<year>]
    
    取值范围 允许的特殊字符
    <seconds> ,有效范围为0-59的整数 , - * /
    <minutes> ,有效范围为0-59的整数 , - * /
    <hours> ,有效范围为0-23的整数 , - * /
    <day-of-month> 月中,有效范围为0-31的整数 , - * / ? L W C
    <month> ,有效范围为1-12的整数或JAN-DEC , - * /
    <day-of-week> 中天,有效范围为1-7的整数或SUN-SAT , - * / ? L C #
    <year> ,可选填项,有效范围为1970-2099年 , - * /
  2. 通配符

    • 各个通配符的含义:
      通配符 含义 示例
      , 表示列出当前域离散值 如:0 0 10 12,15 * *表示每月的12号和15号的10点0分0秒触发
      - 表示当前域的范围 如:0 0 10 12-15 * *表示每月的12号到15号的10点0分0秒触发
      * 表示匹配该域的任意值 在<minutes>域使用 *,即表示每分钟都会触发事件
      / 用来表达一个周期性的表达方式 如:0 10/8 * * * *:每个小时的10分开始,每8分钟触发一次
      ? 表示不确定,只能用在<day-of-month>和<day-of-week>两个域,一般用于两个有关联的域之一上,一个确定了,另外一个不能确定,就只能填写? 如:想在每月的20日触发,不管20日星期几,则只能使用如下写法: 0 0 12 20 * ?, 其中最后一位周天只能用 ?,而不能使用 *,如果使用 * 表示不管星期几都会触发,实际上并不是这样。
      L 表示最后一个当前域的值,只能用在<day-of-month>和<day-of-week>两个域 如:0 0 10 * * 5L表示每月的最后一个周四的10点0分0秒触发
      W 表示有效工作日(周一到周五),只能出现在<day-of-month>域,,系统将在离指定日期的最近的有效工作日触发事件,而且W的寻找不会跨过月份 如:0 0 10 8W * ?表示:如果这个月8号是周一到周五的某天,那么在周五的10点0分0秒触发事件,如果当月8号是周六会在前一天周五即7号的10点触发, 如果是当天是周日,那么就会在最近的工作日即下周一即8号的10点触发;但是如果这里的8是1号,而且这个1号是周六,那么触发的时间只能是下周一即3号的10点触发,如果这个是周日,那就是2号的10点触发
      LW 表示一个月的最后一个工作日
      C 。。。 。。。
      # 。。。 。。。

四、Java中使用Cron ~ Spring-Schedule

  1. 在日常开发的之中,在项目中你或许会遇到定时任务,是一定,那么你会怎么做?是使用Timer Task,还是使用大名鼎鼎的Quatz,还是会选择Spring-Schedule等,答案不重要,重要的是你心里要有根据的取舍和选择,好了,我来补充一个关于定时任务的,由于都是定时任务,所以就放在了Linux的计划任务这篇中,作为一个补充出现。

  2. cron表达式

    • 常用的cron表达式:
      • 每天0点
        0 0 0 * * ?
        
      • 每天23点
        0 0 23 * * ?
        
      • 每6小时
        0 0 */6 * * ?
        
      • 每1小时
        0 0 */6 * * ?
        
      • 每1分钟
        0 */1 * * * ?
        
      • 例01:每隔5秒执行一次:
        */5 * * * * ?
        
      • 例02:每隔5分执行一次:
        0 */5 * * * ?
        
      • 例03:在26分、29分、33分执行一次:
        0 26,29,33 * * * ?
        
      • 例04:每天半夜12点30分执行一次(注意日期域为0不是24):
        0 30 0 * * ?
        
      • 例05:每天凌晨1点执行一次:
        0 0 1 * * ?
        
      • 例06:每天上午10:15执行一次:
        0 15 10 ? * * 或 0 15 10 * * ? 或 0 15 10 * * ? *
        
      • 例07:每天中午十二点执行一次:
        0 0 12 * * ?
        
      • 例08:每天14点到14:59分,每1分钟执行一次:
        0 * 14 * * ?
        
      • 例09:每天14点到14:05分,每1分钟执行一次:
        0 0-5 14 * * ?
        
      • 例10:每天14点到14:55分,每5分钟执行一次:
        0 0/5 14 * * ?
        
      • 例11:每天14点到14:55分,和18点到18点55分,每5分钟执行一次:
        0 0/5 14,18 * * ?
        
      • 例12:每天18点执行一次:
        0 0 18 * * ?
        
      • 例13:每天18点、22点执行一次:
        0 0 18,22 * * ?
        
      • 例14:每天7点到23点,每整点执行一次:
        0 0 7-23 * * ?
        
      • 例15:每个整点执行一次:
        0 0 0/1 * * ?
        
      • 例16:每天凌晨2点
        0 0 2 * * ?
        
      • 例17:每天隔一小时
        0 * */1 * * ?
        
      • 例17:每天12点触发
        0 12 * * ?
        
      • 例18:每天10点15分触发
        15 10 ? * *
        15 10 * * ?
        15 10 * * ? *
        
      • 例19:2005年每天10点15分触发
        15 10 * * ?
        
      • 例20:每天下午的 2点到2点59分每分触发
        * 14 * * ?
        
      • 例21:每天下午的 2点到2点59分(整点开始,每隔5分触发)
        0/5 14 * * ?
        
      • 例22:每天下午的 2点到2点59分(整点开始,每隔5分触发)
        0/5 14,18 * * ?
        
      • 例23:每天下午的 2点到2点05分每分触发
        0-5 14 * * ?
        
      • 例24:3月分每周三下午的 2点10分和2点44分触发 (特殊情况,在一个时间设置里,执行两次或 两次以上的情况)
        10,44 14 ? 3 WED
        
      • 例25:每周5凌晨2点59分触发;
        59 2 ? * FRI
        
      • 例26:从周一到周五每天上午的10点15分触发
        17 10 ? * MON-FRI
        
      • 例27:每月15号上午10点15分触发
        15 10 15 * ?
        
      • 例28:每月最后一天的10点15分触发
        15 10 L * ?
        
      • 例29:每月最后一周的星期五的10点15分触发
        15 10 ? * 6L
        
      • 例30:从2002年到2005年每月最后一周的星期五的10点15分触发
        15 10 ? * 6L 2002-2005
        
      • 例31:每月的第三周的星期五开始触发
        15 10 ? * 6#3
        
      • 例32:每月的第一个中午开始每隔5天触发一次
        0 12 1/5 * ?
        
      • 例33:每年的11月11号 11点11分触发(光棍节)
        11 11 11 11 ?
        
  3. 配置Spring-Schedule

    • 在Spring的配置文件中添加配置:
      <task:annotation-driven/>
      
      • 如果IDE没有添加schema,则需要手动添加:
        xmlns:task="http://www.springframework.org/schema/task"
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
        
  4. 添加计划任务代码:

    import org.shreker.core.service.IOrderService;
    import org.shreker.core.util.PropertiesUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class UpdateOrderStatusTask {
        @Autowired
        private IOrderService iOrderService;
        @Scheduled(cron = "0 */1 * * * ?")
        public void updateOrderTask() {
            int hour = Integer.parseInt(PropertiesUtil.getProperty("order.close.time.hour", "1"));
            iOrderService.closeOrder(hour);
        }
    }
    

五、Cron和CronTab

六、单节点Quartz引入

  1. 引入 pom(如果你的项目是用 SpringBoot,查看一下已经包含了 quartz 的包)
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.1</version>
    </dependency>
    
  2. 编写监听器
    import com.ainemo.dcs.statistics.apimonitor.task.QuartzScheduler;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.SchedulerFactory;
    import org.quartz.impl.StdSchedulerFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.event.ContextRefreshedEvent;
    
    /**
     * created by `ShrekerNil` at 2019/03/29 10:27
     */
    @Configuration
    public class ApplicationStartQuartzJobListener implements ApplicationListener<ContextRefreshedEvent> {
        
        Logger logger = LoggerFactory.getLogger(ApplicationStartQuartzJobListener.class);
        
        @Autowired
        private QuartzScheduler quartzScheduler;
        
        /**
         * 初始启动quartz
         */
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            try {
                logger.info("[<QUZRTZ DISPATCH>]Starting Quartz task ...");
                quartzScheduler.startJob();
            } catch (SchedulerException e) {
                logger.error("[<QUZRTZ DISPATCH>]ERROR: ", e);
            }
        }
        
        /**
         * 初始注入scheduler
         */
        @Bean
        public Scheduler scheduler() throws SchedulerException {
            SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
            return schedulerFactoryBean.getScheduler();
        }
        
    }
    
  3. 编写任务
    import java.util.Date;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    /**
     * created by `ShrekerNil` at 2019/03/29 10:27
     */
    public class Interval10Task implements Job {
        
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("[<QUZRTZ DISPATCH>]开始:" + new Date());
            // TODO 业务
            System.out.println("[<QUZRTZ DISPATCH>]########################################- analysis-origin-log-info-10 -########################################");
            System.out.println("[<QUZRTZ DISPATCH>]结束:" + new Date());
        }
        
    }
    
  4. 编写调度器
    import java.util.Date;
    import org.quartz.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * created by `yanqinglong` at 2019/03/29 10:20
     */
    @Component
    public class QuartzScheduler {
        
        @Autowired
        private Scheduler scheduler;
        
        /**
         * 开始执行所有任务
         */
        public void startJob() throws SchedulerException {
            startJob1(scheduler);
            startJob2(scheduler);
            // TODO 其他的任务
            scheduler.start();
        }
        
        /**
         * 获取Job信息
         */
        public String getJobInfo(String name, String group) throws SchedulerException {
            TriggerKey triggerKey = new TriggerKey(name, group);
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
                    scheduler.getTriggerState(triggerKey).name());
        }
        
        /**
         * 修改某个任务的执行时间
         */
        public boolean modifyJob(String name, String group, String time) throws SchedulerException {
            Date date = null;
            TriggerKey triggerKey = new TriggerKey(name, group);
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            String oldTime = cronTrigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(time)) {
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                        .withSchedule(cronScheduleBuilder).build();
                date = scheduler.rescheduleJob(triggerKey, trigger);
            }
            return date != null;
        }
        
        /**
         * 暂停所有任务
         */
        public void pauseAllJob() throws SchedulerException {
            scheduler.pauseAll();
        }
        
        /**
         * 暂停某个任务
         */
        public void pauseJob(String name, String group) throws SchedulerException {
            JobKey jobKey = new JobKey(name, group);
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.pauseJob(jobKey);
        }
        
        /**
         * 恢复所有任务
         */
        public void resumeAllJob() throws SchedulerException {
            scheduler.resumeAll();
        }
        
        /**
         * 恢复某个任务
         */
        public void resumeJob(String name, String group) throws SchedulerException {
            JobKey jobKey = new JobKey(name, group);
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.resumeJob(jobKey);
        }
        
        /**
         * 删除某个任务
         */
        public void deleteJob(String name, String group) throws SchedulerException {
            JobKey jobKey = new JobKey(name, group);
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.deleteJob(jobKey);
        }
        
        /**
         * 简单调取器
         */
        private void startJob1(Scheduler scheduler) throws SchedulerException {
            JobDetail jobDetail = JobBuilder.newJob(Interval10Task.class).withIdentity("analysis-origin-log-info-10", "Interval10Task").build();
            SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.repeatMinutelyForever();
            //simpleScheduleBuilder.withIntervalInMinutes(10);
            simpleScheduleBuilder.withIntervalInSeconds(10);
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity("analysis-origin-log-info-10", "Interval10Task").withSchedule(simpleScheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, simpleTrigger);
        }
        
        /**
         * 基于 Crontab 的调度器
         */
        private void startJob2(Scheduler scheduler) throws SchedulerException {
            JobDetail jobDetail = JobBuilder.newJob(Interval30Task.class).withIdentity("job3", "group3").build();
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 10 * * * ?");
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job2", "group2").withSchedule(cronScheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, cronTrigger);
        }
        
    }
    
  5. 问题:在Interval10Task中无法注入 Service,解决方案:先获取 ApplicationContext,在通过 ApplicationContext 来获取其中的 Service 对象,这里记一下,先贴出另外一种方案:
    • 编写 Quartz 环境监视器
      import javax.servlet.ServletContext;
      import javax.servlet.ServletContextEvent;
      import org.quartz.SchedulerException;
      import org.quartz.ee.servlet.QuartzInitializerListener;
      import org.quartz.impl.StdSchedulerFactory;
      import org.springframework.stereotype.Component;
      
      /**
       * Quartz 环境监听器
       * created by `ShrekerNil` at 2019/04/03 10:27
       */
      @Component
      public class QuartzServletContextListener extends QuartzInitializerListener {
          
          public static final String CONTEXT_NAME = "servletContext";
          
          @Override
          public void contextDestroyed(ServletContextEvent sce) {
              super.contextDestroyed(sce);
          }
          
          @Override
          public void contextInitialized(ServletContextEvent sce) {
              super.contextInitialized(sce);
              ServletContext servletContext = sce.getServletContext();
              StdSchedulerFactory factory = (StdSchedulerFactory) servletContext.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
              try {
                  factory.getScheduler().getContext().put(QuartzServletContextListener.CONTEXT_NAME, servletContext);
              } catch (SchedulerException e) {
                  e.printStackTrace();
              }
          }
          
      }
      
    • 在 Job 的 execute 方法中添加如下代码
      ServletContext servletContext = null;
      try {
          servletContext = (ServletContext) context.getScheduler().getContext().get(QuartzServletContextListener.CONTEXT_NAME);
      } catch (SchedulerException e1) {
          LOGGER.error("[<QUARTZ DISPATCH>] - ERROR :", e1);
      }
      if (servletContext == null) {
          LOGGER.error("[<QUARTZ DISPATCH>] - ERROR : Cannot load Servlet Context");
          return;
      }
      WebApplicationContext cxt = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
      apiInvocationStatisService = (ApiInvocationStatisService) cxt.getBean("apiInvocationStatisService");
      originLogInfoService = (OriginLogInfoService) cxt.getBean("originLogInfoService");
      

七、集群 Quartz 配置

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容