spring boot @Scheduled未生效原因以及相关坑、及相对其他定时任务架构的优势
在spring boot中,支持多种定时执行模式(cron, fixRate, fixDelay),在Application或者其他Autoconfig上增加@EnableScheduling注解开启。
然后在指定方法增加@Scheduled注解,如下:
@Scheduled(cron="0 0 0/1 * * ?") public void updateTime() { current_log_time_appendix = sdf.format(new Date()); logger.info("日志文件切换, 切换后为:" + current_log_time_appendix); }
需要注意的是,如果在多个函数上使用了@Scheduled,那么一定是一个执行完毕,才能排下一个。这往往不是我们想要的效果。此时需要在Scheduling配置类为schedule返回一个预定的线程池,如下:
@Configuration @EnableScheduling public class SchedulingConfiguration { @Bean(destroyMethod = "shutdown") public Executor taskScheduler() { return Executors.newScheduledThreadPool(10); } }
完成之后,多个@Scheduled可以并发执行了,最高并发度是3,但是同一个@Schedule不会并发执行。
除了cron表达式和quartz、es-job以及linux cron等一样灵活外, spring容器托管的定时任务方法能够利用现成的依赖注入体系,例如:
@Scheduled(cron="0 * * * * *") // 表达式也可以通过@Value注入,例如@Scheduled(cron = "${cron_expression}") public void ss() { XXXParameter<BaseParameter> param = new TaLiquidateParameter<>(); param.setTenantCode("*"); param.setLongTaCode("F6"); param.setDatabaseNo("1"); BaseParameter data = new BaseParameter(); data.setLiqBatchNo("1"); data.setIoFlow("EXPORTCONDATA"); param.setData(data); interfaceDataPrepare(param ); // 当前bean的方法,会依赖其它bean }
其它的方式就必须通过ApplicationContext取主动lookup的方式获取了。但是它的缺点是不支持集群,此时仍然需要其它控制机制,所以用来做测试或者其它不需要集群的场景是不错的。
各种cron表达式备忘如下:
0 * * * * *
:每分钟(当秒为0的时候)0 0 * * * *
:每小时(当秒和分都为0的时候)*/10 * * * * *
:每10秒0 5/15 * * * *
:每小时的5分、20分、35分、50分0 0 9,13 * * *
:每天的9点和13点0 0 8-10 * * *
:每天的8点、9点、10点0 0/30 8-10 * * *
:每天的8点、8点半、9点、9点半、10点0 0 9-17 * * MON-FRI
:每周一到周五的9点、10点…直到17点(含)0 0 0 25 12 ?
:每年12约25日圣诞节的0点0分0秒(午夜)0 30 10 * * ? 2016
:2016年每天的10点半
其中的?
在用法上其实和*
是相同的。但是*
语义上表示全匹配,而?
并不代表全匹配,而是不关心。比如对于0 0 0 5 8 ? 2016
来说,2016年8月5日是周五,?
表示我不关心它是周几。而0 0 0 5 8 * 2016
中的*
表示周一也行,周二也行……语义上和2016年8月5日冲突了,你说谁优先生效呢。
不记得也没关系,记住Cron Maker也可以,它可以在线生成cron表达式。
坑
同时运行
同一个task,如果前一个还没跑完后面一个就不会触发,这没有问题。但是不同的task也不能同时运行就不太合理了。不过其实是scheduler的默认线程数为1的缘故。
解决方法1:如下配置pool-size,但这样会导致同一个task前一个还没跑完后面又被触发的问题。
<task:scheduler id="scheduler" pool-size="2" /> |
解决方法2:让任务分别运行在不同的scheduler里。例如:
<task:scheduler id="myScheduler1"/> <task:scheduler id="myScheduler2"/> <task:scheduled-tasks scheduler="myScheduler1"> <task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/> </task:scheduled-tasks> <task:scheduled-tasks scheduler="myScheduler2"> <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/> </task:scheduled-tasks>