spring-task

spring-task

丁起男 238 2021-11-30

spring-task

sprngtask是spring的任务调度框架,可以实现简单的定时任务调用

上手

  1. 依赖:springboot项目不需要额外进行依赖

  2. 使用注解@EnableScheduling开启springtask

  3. 配置具体任务

    @Component
    @Slf4j
    public class MyTask {
    
        @Scheduled(fixedDelay = 2000)//每2秒触发一次
        public void test1(){
            log.info("test1---》{}", LocalDateTime.now());
        }
    
        @Scheduled(cron = "0/2 * * * * ?")//使用cron表达式,每2秒触发一次
        public void test2(){
            log.info("test2---》{}", LocalDateTime.now());
        }
    }
    

注意:此时执行项目,默认使用同一个现场进行执行,前一个任务执行时间会影响到后一个任务的调用时机

需要额外配置:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //设置2个线程执行任务,这样就允许两个任务并行执行
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(2));
    }
}

动态任务

springtask是可以实现和quartz类似的动态定时任务的

  1. cron表达式类

    //这里是为了简单,实际中可以在数据库中获取
    @Component
    @Data
    public class DynamicCronExpression {
        private String cron = "*/2 * * * * ?";
    }
    
  2. 任务

    @Component
    @Slf4j
    public class MyTask {
    	//这里不需要添加@Scheduled注解
        public void test(){
            log.info("自定义定时任务");
        }
    }
    
  3. 配置类

    @Configuration
    @Slf4j
    public class DynamicScheduleConfig implements SchedulingConfigurer {
    
        //表达式类
        @Autowired
        private DynamicCronExpression dynamicCronExpression;
    	//任务
        @Autowired
        private MyTask myTask;
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            //参数1:当前要执行的任务,参数2:设置任务的触发表达式
            taskRegistrar.addTriggerTask(()->myTask.test(),triggerContext -> {
                log.info("设置当前的cron表达式:{}",dynamicCronExpression.getCron());
                return new CronTrigger(dynamicCronExpression.getCron())
                        .nextExecutionTime(triggerContext);
            }); //设置触发任务
        }
    }
    
  4. 调用方

    @RestController
    @RequestMapping("task")
    @Slf4j
    public class TaskController {
        @Autowired
        private DynamicCronExpression dynamicCronExpression;
    
        @PutMapping("set")
        public void setCorn(String corn){
            log.info("修改corn表达式为:{}",corn);
            dynamicCronExpression.setCron(corn);
        }
    }
    

分布式场景

springtask自身无法胜任分布式场景,需要借助外部组件ShedLock

  1. 导入依赖

    		<!-- shedlock依赖 -->
    		<dependency>
                <groupId>net.javacrumbs.shedlock</groupId>
                <artifactId>shedlock-spring</artifactId>
                <version>4.29.0</version>
            </dependency>
            <!-- shedlock使用redis依赖 -->
            <dependency>
                <groupId>net.javacrumbs.shedlock</groupId>
                <artifactId>shedlock-provider-redis-spring</artifactId>
                <version>4.29.0</version>
            </dependency>
    		<!-- redis依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
  2. 在配置文件中配置redis相关信息

  3. 编写配置类

    @Configuration
    //分布式任务调度如果被强制霸占,那么其它的节点是无法访问的
    @EnableSchedulerLock(defaultLockAtMostFor = "PT30S")//30秒强制释放锁
    public class ShedLockConfig {
    
        @Bean
        public LockProvider lockProvider(RedisConnectionFactory factory){
            //lock是redis中的锁名称,这里直接写死。实际开发中建议使用环境名等
            return new RedisLockProvider(factory,"lock");
        }
    }
    
  4. 在任务上加锁

    @Component
    @Slf4j
    public class ShedLockTask {
    
        @SneakyThrows
        @Scheduled(cron = "*/2 * * * * ?")
        //name:锁名称,相同名称的会互斥
        //lockAtLeastFor:成功执行定时任务时任务节点所能拥有独占锁的最短时间
        //lockAtMostFor:成功执行定时任务时任务节点所能拥有独占锁的最长时间
        @SchedulerLock(name = "task",lockAtLeastFor = "2000")//2秒后开启其它任务
        public void task1(){
            log.info("task1");
            TimeUnit.SECONDS.sleep(1);
        }
    }