zoukankan      html  css  js  c++  java
  • springboot + schedule

    参考文章:https://blog.csdn.net/sinianliushui/article/details/78841713

    参考文章: https://blog.csdn.net/hao7030187/article/details/79077464

    参考文章:https://www.cnblogs.com/domi22/p/9418433.html

    springboot的SchedulerTask相对Quartz来说,简单方便,可试用于小型的job处理。

    因没法持久化因此不支持分布式部署,和动态数据源配置。

    如果要

    一、简易配置

    1. 开启定时任务,在启动类添加以下注解

    @EnableScheduling

    2. 创建并发配置,并发线程

    @Component
    public class SchedulerConfig implements SchedulingConfigurer {
    
        /**
         * 设置定时任务
         * @param scheduledTaskRegistrar
         */
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
        }
    }

    3. 创建scheduler,以下是没5秒触发一次,cron表达式有cron在线生成器可以用

    @Component
    @Slf4j
    public class SchedulerTask {
    
        @Scheduled(cron="0/5 * * * * ? ")
        public void testTask(){
            DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.debug(Thread.currentThread() + "  " + sdf.format(new Date()));
        }
        @Scheduled(cron="0/5 * * * * ? ")
        public void test2Task(){
            DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.debug(Thread.currentThread() + "  " + sdf.format(new Date()));
        }

    }

    二、分开配置

    如下例子,如果只有一个Bean,名字为taskScheduler,或者方法名为taskScheduler则会作为自动的Scheduler。使用 @Scheduled即对应的是此TaskScheduler。

    参见文章:  https://blog.csdn.net/sinianliushui/article/details/78841713

    /**
     * description: 
     * 定时任务
     * @Autor:DennyZhao
     * @Date:2019/2/15
     * @Version: 1.0.0
     */
    @Component
    @EnableScheduling
    public class SchedulerConfig {
        /** task相关的属性文件 **/
        @Autowired
        private TaskProperties taskProperties;
    
        /**
         * 设备组织用Job
         * @return
         */
       @Bean("DeviceJob")
       public TaskScheduler initDeviceOrgTask(){
           ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
           //线程池大小
           scheduler.setPoolSize(taskProperties.getDeviceThreadCount());
           //线程名字前缀
           scheduler.setThreadNamePrefix("taskThread-deviceTask: ");
           scheduler.initialize();
           Trigger trigger = new CronTrigger(taskProperties.getDeviceOrgCron());
           scheduler.schedule(new DeviceOrgTask(), trigger);
           return scheduler;
       }
    
        /**
         * Jpush用Job
         * @return
         */
        @Bean("JpushJob")
        public TaskScheduler initJpushTask(){
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            //线程池大小
            scheduler.setPoolSize(taskProperties.getJpushThreadCount());
            //线程名字前缀
            scheduler.setThreadNamePrefix("taskThread-jpushTask: ");
            scheduler.initialize();
            Trigger trigger = new CronTrigger(taskProperties.getJpushReportCron());
            scheduler.schedule(new JpushReportTask(), trigger);
            return scheduler;
        }
    }

    TaskPropertis

    /**
     * description: 
     * JPUSH执行任务相关参数
     *
     * @Autor:DennyZhao
     * @Date:2019/2/15
     * @Version: 1.0.0
     */
    @Component
    @PropertySource("classpath:task-config.properties")
    @ConfigurationProperties(prefix="task")
    @Data
    public class TaskProperties {
    
        /** 极光推送appKey **/
        private String jpushAppKey;
        /** 极光推送secretKey **/
        private String jpushSecretKey;
    
    
    
        /** 设备JOB的线程数 **/
        private int deviceThreadCount;
        /** 设备组织Cron **/
        private String deviceOrgCron;
        /** Jpush的JOB线程数 **/
        private int jpushThreadCount;
        /** Jpush的推送报告Cron **/
        private String jpushReportCron;
    }

    创建类实现Runable接口即可使用。

    @Component
    @Slf4j
    public class DeviceOrgTask implements Runnable {
    
        @Override
        public void run() {
            DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.debug(Thread.currentThread() + "  " + sdf.format(new Date()));
        }
    }

     

    问题: 

         1.  从以上的一简易配置中(如果不添加SchedulerConfig)也可以运行,发现如果在方法中添加Thread.sleep(),会影响下面方法的运行和下次的运行时间。

            因为默认的 ConcurrentTaskScheduler 计划执行器采用Executors.newSingleThreadScheduledExecutor() 实现单线程的执行器。

            因此要使用异步: 采用异步的方式执行调度任务,配置 Spring 的 @EnableAsync,在执行定时任务的方法上标注 @Async配置任务执行。注意线程池大小要依据单个任务时间和任务间隔。

          2. 分布式重复执行

               1.使用 redis分布式锁setnx命令 来控制是否已经存在有任务在执行。

                2.使用spring的shedLock,创建一个数据表,先更新者执行,非更新者不执行。

                   参见:https://segmentfault.com/a/1190000011975027

          3. 主程序挂掉后,job停止,数据丢失

                使用redis记录执行时间。

     异常:  

    [ERROR] 2019-03-21 14:00:38,757 >>> org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler.handleError(TaskUtils.java:96)
    [massage] Unexpected error occurred in scheduled task.
    java.lang.IllegalStateException: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@8f84321 has been closed already

    使用applicationContext未获取到bean异常。

    因EnabledTask会使得任务在执行完后closeContext导致,在配置文件添加:

    spring.cloud.task.closecontext_enable=false

    高版本用 spring.cloud.task.close_context_enabled=false

    参考文章: https://stackoverflow.com/questions/48933575/spring-cloud-task-scheduling-context-closed

  • 相关阅读:
    bzoj-2748 2748: [HAOI2012]音量调节(dp)
    bzoj-2338 2338: [HNOI2011]数矩形(计算几何)
    bzoj-3444 3444: 最后的晚餐(组合数学)
    codeforces 709E E. Centroids(树形dp)
    codeforces 709D D. Recover the String(构造)
    codeforces 709C C. Letters Cyclic Shift(贪心)
    codeforces 709B B. Checkpoints(水题)
    codeforces 709A A. Juicer(水题)
    Repeat Number
    hdu 1003 Max Sum (动态规划)
  • 原文地址:https://www.cnblogs.com/DennyZhao/p/10385533.html
Copyright © 2011-2022 走看看