基于Redission的分布式注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock{
/**
* 支持@RedisLock(product:${name}:${id}:value)这种方式定义lockKey
* @return
*/
String lockKey() default "";
/**
* 获取锁等待时间,默认3秒
*/
long waitTime() default 3000L;
/**
* 持锁时间(单位毫秒),默认30秒,超过这个时间,自动释放锁
*/
long leaseTime() default 30000L;
}
切面定义
@Aspect
@Slf4j
public class RedisLockAspect {
@Autowired
private RedissonClient redissonClient;
/**
* 切点(控制器中返回值为BASERET且添加RedisLockAop注解的方法)
*/
@Pointcut("@annotation(com.**.*.RedisLock)")
public void lockPoint() {
}
@Around("lockPoint()")
public Object lockAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
String lockKey= DynamicParamParser.handle(method,pjp.getArgs(),redisLock.lockKey());
RLock lock = redissonClient.getLock(lockKey);
if(!lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), TimeUnit.MILLISECONDS)){
log.error(" get lock falied, key:{}",lockKey);
throw new BaseRetException(BaseRet.createFailureRet("get lock falied, key: "+lockKey+""));
}
Object object = null;
try {
object = pjp.proceed();
} catch (Exception e) {
log.error("execute locked method occured an exception", e);
throw e;
} finally {
lock.unlock();
log.debug("release lock [{}]", lockKey);
}
return object;
}
}
Redission配置
@Slf4j
@ConditionalOnClass({Redisson.class})
@EnableConfigurationProperties({RedissonProperties.class, RedisProperties.class})
public class RedissionAutoConfiguration {
@Autowired
private RedisProperties redisProperties;
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private ApplicationContext ctx;
@PostConstruct
public void init() {
log.info("enabled Redission");
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redisson() throws IOException {
Config config = null;
Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties);
int timeout;
if(null == timeoutValue){
timeout = 10000;
}else if (!(timeoutValue instanceof Integer)) {
Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue();
} else {
timeout = (Integer)timeoutValue;
}
if (redissonProperties.getConfig() != null) {
try {
InputStream is = getConfigStream();
config = Config.fromYAML(is);
} catch (IOException e) {
// trying next format
try {
InputStream is = getConfigStream();
config = Config.fromYAML(is);
} catch (IOException e1) {
throw new IllegalArgumentException("Can't parse config", e1);
}
}
} else if (redisProperties.getSentinel() != null) {
Method nodesMethod = ReflectionUtils.findMethod(RedisProperties.Sentinel.class, "getNodes");
Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel());
String[] nodes;
if (nodesValue instanceof String) {
nodes = convert(Arrays.asList(((String)nodesValue).split(",")));
} else {
nodes = convert((List<String>)nodesValue);
}
config = new Config();
config.useSentinelServers()
.setMasterName(redisProperties.getSentinel().getMaster())
.addSentinelAddress(nodes)
.setDatabase(redisProperties.getDatabase())
.setConnectTimeout(timeout)
.setPassword(redisProperties.getPassword());
} else if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
List<String> nodesObject = (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);
String[] nodes = convert(nodesObject);
config = new Config();
config.useClusterServers()
.addNodeAddress(nodes)
.setConnectTimeout(timeout)
.setPassword(redisProperties.getPassword());
} else {
config = new Config();
String prefix = "redis://";
Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
if (method != null && (Boolean) ReflectionUtils.invokeMethod(method, redisProperties)) {
prefix = "rediss://";
}
config.useSingleServer()
.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
.setConnectTimeout(timeout)
.setDatabase(redisProperties.getDatabase())
.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
private String[] convert(List<String> nodesObject) {
List<String> nodes = new ArrayList<String>(nodesObject.size());
for (String node : nodesObject) {
if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
nodes.add("redis://" + node);
} else {
nodes.add(node);
}
}
return nodes.toArray(new String[nodes.size()]);
}
private InputStream getConfigStream() throws IOException {
Resource resource = ctx.getResource(redissonProperties.getConfig());
InputStream is = resource.getInputStream();
return is;
}
@Bean
public RedisLockAspect redisLockAspect(){
return new RedisLockAspect();
}
}
@Data
@ConfigurationProperties(prefix = "spring.redis.redisson")
public class RedissonProperties {
private String config;
public static final String ADDR_PREFIX="redis://";
}