这是我第五篇原创文章,计划写定时任务内容,想想只要打开自己的有道云笔记,复制粘贴,整理排版一下就能轻松搞定了,这样做有意义吗?自己写文章的价值点是什么呢?考虑了很长时间,主要希望做到一下几点:
1.思路分析
我一直觉得作为技术人员,技术思维比技术本身更重要,工作中遇到难题无法解决,没思路才是最可怕的。解决问题的思维能力特别考验技术人内功、经验和技术积累。
现在如何需要获取某技术知识,很多知识点可以通过网络获取,而且获取成本越来越低,例如目前很火的极客时间,里面很多大牛的课程,价格已经非常低,想想3年前我想找付费知识学习,都是几百几千的费用,只能在淘宝淘盗版。很多问题是想不到,如果有人告诉思路,就算里面的技术点不明白,交给搜索引擎就是了。
因此,
上篇文章重复请求的幂等接口设计的思考(二)提到使用定时任务扫描数据库中状态值为失败的业务定时再次调用接口。此时项目中未有现成的定时任务案例,该如何做呢?我的思路如下:
在主类上使用@EnableScheduling注解开启对定时任务的支持
在定时任务的类或者方法上添加@Async实现异步
cron时间动态配置到配置文件中
@Component
@Async
public class SchedulingTest {
@Value("${paramInfo}")
private String paramInfo;
private static Logger log = LoggerFactory.getLogger(SchedulingTest.class);
@Scheduled(cron = "${cron.time}")
public void queryPayStatus() {
log.info("执行定时任务queryPayStatus---start");
log.info("执行定时任务queryPayStatus---end");
}
}
1.新建SchedulerCondition类,配置定时任务开启条件
scheduling.enabled对应到配置文件中的值
scheduling.enabled=true 开启
scheduling.enabled=false 关闭
public class SchedulerCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
boolean enable = Boolean.valueOf(conditionContext.getEnvironment().getProperty("scheduling.enabled"));
return enable;
}
}
自定义启动类
@Configuration
public class Scheduler {
@Conditional(SchedulerCondition.class)
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
这样就完成了配置文件中scheduling.enabled的值来控制定时任务的启动与关闭。
@Configuration
@EnableAsync
public class AsyncConfig {
@Value("${core.pool.size}")
private int corePoolSize;
@Value("${max.pool.size}")
private int maxPoolSize;
@Value("${queue.capacity}")
private int queueCapacity;
@Value("${keep.alive}")
private int keepAlive;
// 核心线程数(setCorePoolSize)10:线程池创建时候初始化的线程数
// 最大线程数(setMaxPoolSize)20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
// 缓冲队列(setQueueCapacity)200:用来缓冲执行任务的队列
// 允许线程的空闲时间(setKeepAliveSeconds)60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
// 线程池名的前缀(setThreadNamePrefix):设置好了之后可以方便我们定位处理任务所在的线程池
// 线程池对拒绝任务的处理策略(setRejectedExecutionHandler):这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute
// 方法的调用线程中运行被拒绝的任务(setWaitForTasksToCompleteOnShutdown);如果执行程序已关闭,则会丢弃该任务
// setWaitForTasksToCompleteOnShutdown(true)该方法就是这里的关键,用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁。
// 同时,这里还设置了setAwaitTerminationSeconds(60),该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAlive);
executor.setThreadNamePrefix("collect-");//自定义线程名称
// 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//调度器shutdown被调用时等待当前被调度的任务完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//等待时长
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
每周星期天凌晨1点实行一次:0 0 1 ? * L
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
“0 15 10 * * ? 2005” 2005年的每天上午10:15 触发
详细含义可以网上查资料
SpringBoot几种定时任务的实现方式
推荐XXL-JOB的学习文档
XXL-JOB
重复请求的幂等接口设计的思考(一)
三年程序员的第一篇公号文章
自动化远程登陆操作和传输文件(scp ssh expect)
长按二维码关注,阅读我的程序员故事