第一步. 自动配置类
- 主启动类添加:@EnableScheduling //开启定时任务
aop和redis POM添加:
<!--redis驱动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--aop面向切面编程-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义分布式定时任务注解
/**
* @program:
* @description: 定时任务锁
* @author: wangZhiDong
* @created: 2021/11/29 09:06
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TaskLock {
/**
* 定时任务名称
* @return
*/
String name() default "";
/**
* redis缓存key值
* @return
*/
String key();
/**
* 过期时间单位s (自动解锁时间,防止死锁)
* @return
*/
int expired();
/**
* 执行完毕是否解锁
* @return
*/
boolean unLock() default true;
}
定义 分布式定时任务 切面具体实现
/**
* @program:
* @description: 定时任务锁切面
* @author: wangZhiDong
* @created: 2021/10/29 09:12
*/
@Aspect
@Component
@Slf4j
@AllArgsConstructor
@Order(10)
public class TaskLockAspect {
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.wang.timedtask.annotation.TaskLock)")
public void TaskLockAspect() {
}
@Around("TaskLockAspect() && @annotation(taskLock)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint,TaskLock taskLock) throws Throwable {
String value = UUID.randomUUID().toString();
try {
if (lock(taskLock.key(), value, taskLock.expired())) {
return proceedingJoinPoint.proceed();
}
} catch (Exception e) {
log.error("定时任务执行失败,{}", taskLock.key(), e);
} finally {
// 执行完毕解除锁
if (!taskLock.unLock()) {
return null;
}
String lockValue = getLockValue(taskLock.key());
if (StringUtils.isEmpty(lockValue) || !lockValue.equals(value)) {
return null;
}
// 解锁设置为延时1S
unLock(taskLock.key());
}
return null;
}
/**
* 加锁
* @param key
* @param value
* @param time
* @return
*/
public boolean lock(String key , String value , int time){
return redisTemplate.opsForValue().setIfAbsent(key , value , time , TimeUnit.SECONDS);
}
/**
* 解锁
* @param key
*/
public void unLock(String key){
redisTemplate.expire(key , 1 , TimeUnit.SECONDS);
}
/**
* 获取锁值
* @param key
* @return
*/
public String getLockValue(String key){
return (String) redisTemplate.opsForValue().get(key);
}
}
简单使用
/**
* @Author wangZhiDong
* @Date 2021/8/22
* 启动数据纠正
**/
@Slf4j
@Component
public class TransferService {
public static int num = 0;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Scheduled(cron = "0/5 * * * * ?")
@TaskLock(name = "测试分布式定时任务5秒执行一次", key = "test_task",expired = 5)
public void start(){
num++;
System.out.println(LocalDateTime.now().format(formatter)+"第"+num+"次执行的是第一个");
}
/**
* 使用 public 的 定时任务才行
*/
@Scheduled(cron = "0/5 * * * * ?")
@TaskLock(name = "测试分布式定时任务5秒执行一次", key = "test_task",expired = 5)
public void start2(){
TransferService.num++;
System.out.println(LocalDateTime.now().format(formatter)+"第"+TransferService.num+"次执行的是第二个");
}
@Scheduled(cron = "0/5 * * * * ?")
@TaskLock(name = "测试分布式定时任务5秒执行一次", key = "test_task",expired = 5)
public void start3(){
TransferService.num++;
System.out.println(LocalDateTime.now().format(formatter)+"第"+TransferService.num+"次执行的是第三个");
}
}
执行结果:
2021-12-01 14:35:40第1次执行的是第一个
2021-12-01 14:35:45第2次执行的是第二个
2021-12-01 14:35:50第3次执行的是第三个
2021-12-01 14:35:55第4次执行的是第三个
2021-12-01 14:36:00第5次执行的是第二个
2021-12-01 14:36:05第6次执行的是第一个
2021-12-01 14:36:10第7次执行的是第三个
2021-12-01 14:36:15第8次执行的是第一个
2021-12-01 14:36:20第9次执行的是第二个
2021-12-01 14:36:25第10次执行的是第三个
2021-12-01 14:36:30第11次执行的是第一个
...