定时任务(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 
      
    • 查看执行日志
      tail -f /var/log/cron
      

五、Corn表达式的编写

  1. Corn表达式组成:

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

    • 各个通配符的含义:
      通配符 含义 示例
      , 表示列出当前域离散值 如:0 0 10 12,15 * *表示每月的12号和15号的10点0分0秒触发
      - 表示当前域的范围 如:0 0 10 12-15 * *表示每月的12号到15号的10点0分0秒触发
      * 表示匹配该域的任意值 域使用 *,即表示每分钟都会触发事件
      / 用来表达一个周期性的表达方式 如:0 10/8 * * * *:每个小时的10分开始,每8分钟触发一次
      ? 表示不确定,只能用在两个域,一般用于两个有关联的域之一上,一个确定了,另外一个不能确定,就只能填写? 如:想在每月的20日触发,不管20日星期几,则只能使用如下写法: 0 0 12 20 * ?, 其中最后一位周天只能用 ?,而不能使用 *,如果使用 * 表示不管星期几都会触发,实际上并不是这样。
      L 表示最后一个当前域的值,只能用在两个域 如:0 0 10 * * 5L表示每月的最后一个周四的10点0分0秒触发
      W 表示有效工作日(周一到周五),只能出现在域,,系统将在离指定日期的最近的有效工作日触发事件,而且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的配置文件中添加配置:
      
      
      • 如果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 的包)
    
        org.quartz-scheduler
        quartz
        2.2.1
    
    
  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 {
        
        Logger logger = LoggerFactory.getLogger(ApplicationStartQuartzJobListener.class);
        
        @Autowired
        private QuartzScheduler quartzScheduler;
        
        /**
         * 初始启动quartz
         */
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            try {
                logger.info("[]Starting Quartz task ...");
                quartzScheduler.startJob();
            } catch (SchedulerException e) {
                logger.error("[]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("[]开始:" + new Date());
            // TODO 业务
            System.out.println("[]########################################- analysis-origin-log-info-10 -########################################");
            System.out.println("[]结束:" + 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("[] - ERROR :", e1);
      }
      if (servletContext == null) {
          LOGGER.error("[] - 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 配置

你可能感兴趣的:(定时任务(Java Scheduled Task & Quartz))