最近有一个新项目需要写一个定时任务,因为没有涉及到复杂的功能需求,所以考虑仅使用 Spring 自带的 @Scheduled
注解实现
@Scheduled
基于注解实现定时任务的方式很简单,只需要在启动类前加上 @EnableScheduling
注解
1 2 3 4 5 6
| @EnableScheduling public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
|
然后在需要执行定时任务的方法前加上 @Scheduled()
注解,然后在括号内添加规则即可
1 2 3 4 5 6 7 8 9
| 每隔5秒执行一次:*/5 * * * * ? 每隔1分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月1号凌晨1点执行一次:0 0 1 1 * ? 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
@Scheduled(cron = "0 0 2 * * ?") public void func(){}
|
需要注意的是,通过 @Scheduled
实现的定时任务都是在一个线程中执行的,如果出现下一个定时任务开始时前一个还没有执行完的情况,下一个定时任务会阻塞直到前面的定时任务执行完。
此外, @Scheduled
不支持分布式的情况,如果部署到集群上,多台机器都会执行定时任务。所以如果在分布式的情况下,需要通过分布式锁来保证只有一台机器执行定时任务。
@Retryable
考虑到定时任务可能有执行失败的情况,执行失败后需要有重试机制。重试的最简单执行方式就是代码层面使用循环,为了保证业务逻辑的整洁,考虑在框架层面实现重试功能,所以选择使用 @Retryable
注解。
首先需要添加相关依赖
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
|
然后在项目启动类前添加 @EnableRetry
注解
1 2 3 4 5 6 7
| @EnableScheduling @EnableRetry public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
|
然后在方法前加上 @Retryable
注解声明重试条件,当方法抛出对应异常后会触发重试
1 2 3 4 5 6 7 8 9
| @Retryable参数的意思说明 value:抛出指定异常才会重试 include:和value一样,默认为空,当exclude也为空时,默认所以异常 exclude:指定不处理的异常 maxAttempts:最大重试次数,默认3次 backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5)) public void func(){}
|
如果达到最大重试次数依然抛出异常,可以通过 @Recover
注解来配置回调方法,可以基于此实现报警等功能
1 2
| @Recover public void alert(){}
|
注意:重试方法和回调方法需要写在同一个 Java 文件中
TODO
基于分布式锁让 @Schedule
兼容分布式
基于线程池让定时任务并发执行
参考
011 @Retryable的使用 - 曹军 - 博客园