Spring Task执行
① 开启定时任务
开启定时任务只需要在 Spring Boot 的启动类上声明 @EnableScheduling
即可,实现代码如下:
1 2 3 4 5
| @SpringBootApplication @EnableScheduling public class DemoApplication { }
|
② 添加定时任务
定时任务的添加只需要使用 @Scheduled
注解标注即可,如果有多个定时任务可以创建多个 @Scheduled
注解标注的方法,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11
| import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;
@Component public class TaskUtils { @Scheduled(cron = "59 59 23 0 0 5") public void doTask(){ System.out.println("我是定时任务~"); } }
|
注意:定时任务是自动触发的无需手动干预,也就是说 Spring Boot 启动后会自动加载并执行定时任务。
Spring Task 的实现需要使用 cron 表达式来声明执行的频率和规则,具体可百度。
知识扩展:分布式定时任务
上面的方法都是关于单机定时任务的实现,如果是分布式环境可以使用 Redis 来实现定时任务。
使用 Redis 实现延迟任务的方法大体可分为两类:通过 ZSet 的方式和键空间通知的方式。
① ZSet 实现方式
通过 ZSet 实现定时任务的思路是,将定时任务存放到 ZSet 集合中,并且将过期时间存储到 ZSet 的 Score 字段中,然后通过一个无线循环来判断当前时间内是否有需要执行的定时任务,如果有则进行执行,具体实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import redis.clients.jedis.Jedis; import utils.JedisUtils; import java.time.Instant; import java.util.Set;
public class DelayQueueExample { private static final String _KEY = "myTaskQueue"; public static void main(String[] args) throws InterruptedException { Jedis jedis = JedisUtils.getJedis(); long delayTime = Instant.now().plusSeconds(30).getEpochSecond(); jedis.zadd(_KEY, delayTime, "order_1"); jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2"); jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3"); jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4"); jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5"); doDelayQueue(jedis); }
public static void doDelayQueue(Jedis jedis) throws InterruptedException { while (true) { Instant nowInstant = Instant.now(); long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); long nowSecond = nowInstant.getEpochSecond(); Set<String> data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond); for (String item : data) { System.out.println("消费:" + item); } jedis.zremrangeByScore(_KEY, lastSecond, nowSecond); Thread.sleep(1000); } } }
|
② 键空间通知
我们可以通过 Redis 的键空间通知来实现定时任务,它的实现思路是给所有的定时任务设置一个过期时间,等到了过期之后,我们通过订阅过期消息就能感知到定时任务需要被执行了,此时我们执行定时任务即可。
默认情况下 Redis 是不开启键空间通知的,需要我们通过 config set notify-keyspace-events Ex
的命令手动开启,开启之后定时任务的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub; import utils.JedisUtils;
public class TaskExample { public static final String _TOPIC = "__keyevent@0__:expired"; public static void main(String[] args) { Jedis jedis = JedisUtils.getJedis(); doTask(jedis); }
public static void doTask(Jedis jedis) { jedis.psubscribe(new JedisPubSub() { @Override public void onPMessage(String pattern, String channel, String message) { System.out.println("收到消息:" + message); } }, _TOPIC); } }
|