分布式-技术专区-Redis分布式锁实现-第二步
1.利用aop实现分布式锁
2.只用在方法上加个注解,同时加上了重试机制
1.前提我们可以个性化定制一些注解例如:RedisLock注解操作
@RedisLock(lockPrefix = AbstractRedisContants.DIST_LOCK_FUND, lockParameter = "fundId")
public void handle(FundAmountOptTypeEnum optType, Long fundId, BigDecimal amount, String operator, String remark) {
strategyMap.get(optType).handle(fundId, amount, operator, remark);
}
2.定义RedisLock注解开发服务
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import org.springframework.core.annotation.Order;
/**
* redis锁注解
* @date 2018/12/27
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Order(value = 10)
public @interface RedisLock {
// 锁前缀
String lockPrefix() default "";// 方法参数名(用于取参数名的值与锁前缀拼接成锁名),尽量不要用对象map等,对象会toString后与锁前缀拼接
String lockParameter() default "";// 尝试加锁,最多等待时间(毫秒)
long lockWait() default 3000L;// 自动解锁时间 (毫秒)
long autoUnlockTime() default 10000L;// 重试次数
int retryNum() default 0;// 重试等待时间 (毫秒)
long retryWait() default 500L;}
@Order(value = 10)
当使用了@Transactional 或其它切面时,相当于在执行执行多次次AOP切面。那么我们需要通过order 属性去定义AOP切面的先后执行顺序。 order越小,在AOP的chain 中越靠前,越先执行。(chain模式)
3.RedisLockAspect
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;import com.feitai.jieya.server.common.annotation.RedisLock;
import com.feitai.jieya.server.common.exception.BusinessException;
import com.feitai.jieya.server.utils.StringUtils;import lombok.extern.slf4j.Slf4j;
/**
* Description: 分布式锁
* <p>
* 先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试) 如果没有重试次数,方法返回null 记得捕获NP 当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔
* </p>
*/
@Aspect
@Component
@Slf4j
@Order(10)
public class RedisLockAspect {private static final String LOCK_NAME = "lockName";
private static final String lOCK_WAIT = "lockWait";
private static final String AUTO_UNLOCK_TIME = "autoUnlockTime";
private static final String RETRY_NUM = "retryNum";
private static final String RETRY_WAIT = "retryWait";/**
* redis工具类
*/
@Autowired
private RedissonClient redissonClient;@Pointcut("@annotation(com.feitai.jieya.server.common.annotation.RedisLock)")
public void lockAspect() {}@Around("lockAspect()")
public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable {// 获取注解中的参数
Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
String lockName = (String)annotationArgs.get(LOCK_NAME);
Assert.notNull(lockName, "分布式,锁名不能为空");
int retryNum = (int)annotationArgs.get(RETRY_NUM);
long retryWait = (long)annotationArgs.get(RETRY_WAIT);
long lockWait = (long)annotationArgs.get(lOCK_WAIT);
long autoUnlockTime = (long)annotationArgs.get(AUTO_UNLOCK_TIME);// 获取锁
RLock lock = redissonClient.getLock(lockName);
try {
boolean res = lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS);
if (res) {
// 执行主逻辑
return proceeding.proceed();} else {
// 如果重试次数为零, 则不重试
if (retryNum <= 0) {
log.info(String.format("{%s}已经被锁, 不重试", lockName));
throw new BusinessException(String.format("{%s}已经被锁, 不重试", lockName));
}if (retryWait == 0) {
retryWait = 200L;
}
// 设置失败次数计数器, 当到达指定次数时, 返回失败
int failCount = 1;
while (failCount <= retryNum) {
// 等待指定时间ms
Thread.sleep(retryWait);
if (lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS)) {
// 执行主逻辑
return proceeding.proceed();
} else {
log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", lockName, failCount, retryNum,retryWait));
failCount++;
}
}
throw new BusinessException("系统繁忙, 请稍等再试");
}
} catch (Throwable throwable) {
log.error(String.format("执行分布式锁发生异常锁名:{%s},异常名称:{%s}", lockName, throwable.getMessage()));
throw throwable;
} finally {
lock.unlock();
}
}/**
* 获取锁参数
*
* @param proceeding
* @return
*/
private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
// if (!(objs[i] instanceof ExtendedServletRequestDataBinder)
// && !(objs[i] instanceof HttpServletResponseWrapper)) {proceeding.getArgs();
Object[] objs = proceeding.getArgs();
String[] argNames = ((MethodSignature)proceeding.getSignature()).getParameterNames(); // 参数名Class target = proceeding.getTarget().getClass();
Method[] methods = target.getMethods();
String methodName = proceeding.getSignature().getName();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Map<String, Object> result = new HashMap<String, Object>();
RedisLock redisLock = method.getAnnotation(RedisLock.class);if (StringUtils.isNotBlank(redisLock.lockParameter())) {
for (int i = 0; i < objs.length; i++) {
if (redisLock.lockParameter().equals(argNames[i])) {
result.put(LOCK_NAME, redisLock.lockPrefix() + objs[i]);
break;
}}
} else {
result.put(LOCK_NAME, redisLock.lockPrefix());
}
result.put(lOCK_WAIT, redisLock.lockWait());
result.put(AUTO_UNLOCK_TIME, redisLock.autoUnlockTime());
result.put(RETRY_NUM, redisLock.retryNum());
result.put(RETRY_WAIT, redisLock.retryWait());return result;
}
}
throw new RuntimeException("异常");}
本文来自博客园,作者:洛神灬殇,转载请注明原文链接:https://www.cnblogs.com/liboware/p/11945005.html,任何足够先进的科技,都与魔法无异。