方案一:
思路:放置一个开关标识,然后在启动项目时,spring会自动加载这个定时任务类,通过类上的注解判断是否加载该实例,如果不加载,就不会执行定时任务
缺点:只能指定某一个服务【单体情况适用】,集群下还是不能解决该问题
在yml配置文件中添加 例如:
scheduling:
enabled: true
然后在定时任务类上添加注解
@Component
@ConditionalOnProperty(prefix = "scheduling",name="enabled",havingValue="true") //该注解满足参数条件才会加载该类 ,配置文件是true, havingValue="false"该类就不会执行定时任务
public class task{
}
方案二(本次问题使用的方案):
添加redis 分布式锁
首先先添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件中 配置redis 参数
创建redis分布式锁工具类
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* @ClassName RedisLock
* @Description
* @Author zsw
* @Date 2021/1/21 16:48
* @Version V1.0
**/
@Slf4j
public class RedisLock implements AutoCloseable {
private RedisTemplate redisTemplate;
private String key;
private String value;
private int expireTime;
public RedisLock(RedisTemplate redisTemplate, String key, int expireTime) {
this.redisTemplate = redisTemplate;
this.key = key;
this.value = UUID.randomUUID().toString()+"_"+ IPUtil.getLocalIp();
this.expireTime = expireTime;
}
public boolean getLock(){
RedisCallback<Boolean> redisCallback = connection ->{
RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
Expiration seconds = Expiration.seconds(expireTime);
byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
byte[] redisValue = redisTemplate.getKeySerializer().serialize(value);
Boolean result = connection.set(redisKey, redisValue, seconds, setOption);
return result;
};
boolean execute = (boolean)redisTemplate.execute(redisCallback);
return execute;
}
public boolean unLock(){
StringBuffer sb = new StringBuffer();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n");
sb.append("return redis.call(\"del\",KEYS[1])\n");
sb.append("else \n");
sb.append("return 0\n");
sb.append("end");
RedisScript of = RedisScript.of(sb.toString(), Boolean.class);
List<String> keys = Arrays.asList(key);
boolean execute = (boolean)redisTemplate.execute(of, keys, value);
return execute;
}
@Override
public void close() throws Exception {
}
}
如何使用呢?
public class task{
@Scheduling(fixedRate = 1000 * 10)
public void task1(){
try(RedisLock redisLock = new RedisLock(redisTemplate,"key",1000 *9 )){ //定时任务是每次隔10s执行,锁是9秒后就过期释放了或者执行完就释放了
if(redisLock .getLock()){
//执行业务...
}catch(Exception e){
}
}
}
}
备注: 方案一和方案二也可以搭配使用
其他方案:待研究quartz。。。