添加分布式部署定时任务

This commit is contained in:
梁琦涛
2023-04-11 16:32:00 +08:00
parent ccec2afad1
commit 3619d8de54
5 changed files with 237 additions and 0 deletions
@@ -0,0 +1,57 @@
package com.jero.common.aspect;
import com.jero.common.aspect.annotation.RedisLock;
import com.jero.common.exception.JeroBootException;
import com.jero.common.util.RedisLockHelper;
import com.jero.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;
/**
* 分布式部署定时任务
* @Author lqt
*/
@Slf4j
@Aspect
@Component
public class LockMethodAspect {
@Resource
private RedisLockHelper redisLockHelper;
@Resource
private RedisUtil redisUtis;
@Around("@annotation(com.jero.common.aspect.annotation.RedisLock)")
public Object around(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
String value = UUID.randomUUID().toString();
log.info("====value=====" + value + "===========");
String key = redisLock.key();
log.info("====key======" + key + "===========");
try {
final boolean islock = redisLockHelper.lock(redisUtis, key, value, redisLock.expire(), redisLock.timeUnit());
log.info("isLock : {}", islock);
if (!islock) {
log.error("获取锁失败");
throw new JeroBootException("获取锁失败");
}
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throw new JeroBootException("系统异常");
}
} finally {
log.info("释放锁");
redisLockHelper.unlock(redisUtis, key, value);
}
}
}
@@ -0,0 +1,24 @@
package com.jero.common.aspect.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @author LQT
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RedisLock {
/*** 业务键** @return*/
String key();
/*** 锁的过期秒数,默认是5秒** @return*/
int expire() default 5;
/*** 尝试加锁,最多等待时间** @return*/
long waitTime() default Long.MIN_VALUE;
/*** 锁的超时时间单位** @return*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
@@ -0,0 +1,116 @@
package com.jero.common.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class RedisLockHelper {
private long sleepTime = 100;
/**
* 直接使用setnx + expire方式获取分布式锁
* 非原子性
*
* @param key
* @param value
* @param timeout
* @return
*/
public boolean lockSetNx(RedisUtil redisTemplate, String key, String value, int timeout) {
if ( redisTemplate.setnx(key, value)) {
return redisTemplate.expire(key, timeout);
} else {
return false;
}
}
/**
* 使用Lua脚本,脚本中使用setnex+expire命令进行加锁操作
*
* @param redisTemplate
* @param key
* @param uniqueId
* @param seconds
* @return
*/
public boolean lockWithLua(RedisUtil redisTemplate, String key, String uniqueId, int seconds) {
if(!Objects.isNull(seconds)){
log.info("未使用参数:" + seconds);
}
String luaScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" +
"redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end";
Object result = redisTemplate.eval(RedisScript.of(luaScript), Collections.singletonList(key), uniqueId);
//判断是否成功
return result.equals(1L);
}
/**
* 在Redis的2.6.12及以后中,使用 set key value [NX] [EX] 命令
*
* @param key
* @param value
* @param timeout
* @return
*/
public boolean lock(RedisUtil redisTemplate, String key, String value, int timeout, TimeUnit timeUnit) {
long seconds = timeUnit.toSeconds(timeout);
return redisTemplate.setnx(key, value, seconds);
}
/**
* 自定义获取锁的超时时间
*
* @param redisTemplate
* @param key
* @param value
* @param timeout
* @param waitTime
* @param timeUnit
* @return
* @throws InterruptedException
*/
public boolean lockWithWaitTime(RedisUtil redisTemplate, String key, String value, int timeout, long waitTime, TimeUnit timeUnit) throws InterruptedException {
long seconds = timeUnit.toSeconds(timeout);
while (waitTime >= 0) {
if (redisTemplate.setnx(key, value, seconds)) {
return true;
}
waitTime -= sleepTime;
Thread.sleep(sleepTime);
}
return false;
}
/**
* 错误的解锁方法—直接删除key
*
* @param key
*/
public void unlockWithDel(RedisUtil redisTemplate, String key) {
redisTemplate.del(key);
}
/**
* 使用Lua脚本进行解锁操纵,解锁的时候验证value值
*
* @param redisTemplate
* @param key
* @param value
* @return
*/
public boolean unlock(RedisUtil redisTemplate, String key, String value) {
String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then " +
"return redis.call('del',KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript =new DefaultRedisScript<> ();
redisScript.setScriptText(luaScript);
// 这个值类型要跟lua返回值类型一致才行,否则就会报 java.lang.IllegalStateException
redisScript.setResultType(Long.class);
return redisTemplate.eval(redisScript, Collections.singletonList(key), value);
}
}
@@ -1,10 +1,12 @@
package com.jero.common.util;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -609,4 +611,37 @@ public class RedisUtil {
e.printStackTrace();
}
}
/**
* setnx
*/
public boolean setnx(String key, String value) {
try {
return redisTemplate.opsForValue().setIfAbsent(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* setnx
*/
public boolean setnx(String key, String value,long time) {
try {
return redisTemplate.opsForValue().setIfAbsent(key,value, Duration.ofSeconds(time));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* eval
*/
public Boolean eval(RedisScript<Long> luaScripts, List<String> keys, String values) {
Long flag = redisTemplate.execute(luaScripts, keys, values);
//判断是不是为1
return flag == 1L;
}
}
@@ -1,12 +1,16 @@
package com.jero.modules.quartz.job;
import com.jero.common.aspect.annotation.RedisLock;
import com.jero.common.util.DateUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
/**
* 示例带参定时任务
*
@@ -25,6 +29,7 @@ public class SampleParamJob implements Job {
}
@Override
@RedisLock(key = "lock----TestJob",expire=60,timeUnit= TimeUnit.SECONDS)
public void execute(JobExecutionContext jobExecutionContext) {
log.info(" Job Execution key"+jobExecutionContext.getJobDetail().getKey());
log.info("welcome " + this.parameter + " Jero-Boot 带参数定时任务 SampleParamJob ! 时间:" + DateUtils.now());