本来想用 qutarz 的分布式任务调度,奈何加了10来张表太费劲。
只想简单实现一下多个server下别重复或者说同时调用
而且就只个简单的任务 于是用spring自带的定时任务
但有个问题。集群环境会重复执行 且可能是同时执行。这不科学。
先前的考虑是
@ConditionalOnProperty(prefix="cn.com.ur",name="task-enable",havingValue="true")
当task-enable为true的时候才启任务。这样可以配合maven 部署的时候决定 哪台服务拥有任务
但有个问题。。多个开发 都默认task-enable=true 还是重复执行,导致某些业务可能会出错。
没办法 还是要集群环境调度不可控的问题。
spring boot redis 代码如下
package cn.com.ur.mall.service.job;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 分布式任务执行器
*
* @author liucf
*
*/
public class RedisLockTaskActuator {
private Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 提前多少秒使锁失效
*/
private long reduceTime = 5;
private String lockName = "LOCK";
private StringRedisTemplate redisTemplate;
public RedisLockTaskActuator(StringRedisTemplate redisTemplate) {
super();
this.redisTemplate = redisTemplate;
}
public long getReduceTime() {
return reduceTime;
}
public void setReduceTime(long reduceTime) {
this.reduceTime = reduceTime;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public StringRedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@FunctionalInterface
public interface RedisLockTask{
public void run();
}
public void execute(String key, long lockTime,RedisLockTask redisLockTask) {
execute(this.redisTemplate,key,lockTime,this.reduceTime,this.lockName,redisLockTask);
}
public void execute(StringRedisTemplate redisTemplate, String key, long lockTime,RedisLockTask redisLockTask) {
execute(redisTemplate,key,lockTime,this.reduceTime,this.lockName,redisLockTask);
}
public void execute(String key, long lockTime,String lockName,RedisLockTask redisLockTask) {
execute(this.redisTemplate,key,lockTime,this.reduceTime,this.lockName,redisLockTask);
}
public void execute(StringRedisTemplate redisTemplate, String key, long lockTime,String lockName,RedisLockTask redisLockTask) {
execute(redisTemplate,key,lockTime,this.reduceTime,lockName,redisLockTask);
}
public void execute(String key, long lockTime,long reduceTime,String lockName,RedisLockTask redisLockTask) {
execute(this.redisTemplate,key,lockTime,this.reduceTime,lockName,redisLockTask);
}
public void execute(StringRedisTemplate redisTemplate, String key, long lockTime,long reduceTime,String lockName,RedisLockTask redisLockTask) {
boolean lock = redisTemplate.opsForValue().setIfAbsent(key, lockName);
//获取到锁
if(lock) {
//设置超时时间
redisTemplate.expire(key, lockTime - reduceTime, TimeUnit.SECONDS);
//执行
redisLockTask.run();
}else {
//未获取到锁
//为没么没有获取到锁
long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
log.info("{}离一下次执行时间还有:{}秒", key,expire);
if(expire == -1) {//未设置超时时间 //获取锁 设置超时时间失败
redisTemplate.expire(key, lockTime - reduceTime, TimeUnit.SECONDS);
//执行
redisLockTask.run();
}
}
}
}
使用代码如下
@Bean
public RedisLockTaskActuator createRedisLockTaskActuator(StringRedisTemplate template) {
return new RedisLockTaskActuator(template);
}
@Scheduled(cron="0 0/5 * * * ? ")
public void execute() {
redisLockTaskActuator.execute("lockName", 5*60, ()->{
//干点啥
});
}
误差时间 可能根据实际情况调整一下。主要是怕设置锁失效与获取锁的时间差太大,这样基本能满足不重复执行的需求