1:pom.xml添加依赖
xml
复制代码
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.21.1</version>
</dependency>
2-1:方法一:读取默认yml配置
yml
复制代码
# redis配置
spring:
redis:
database: 0
host: 127.0.0.1
password: 123456
port: 6379
2-2:redisson.yml文件的方式
yml
复制代码
spring:
redis:
redisson:
file: classpath:redisson.yml
2-3:redisson.yml
yml
复制代码
# 单节点配置
singleServerConfig:
# 数据库编号
database: 0
# 节点地址
address: redis://127.0.0.1:6379
# 密码
password: 123456
yml
复制代码
# 集群模式
clusterServersConfig:
# 集群节点地址
nodeAddresses:
- "redis://127.0.0.1:16379"
- "redis://127.0.0.1:26379"
- "redis://127.0.0.1:36379"
password: 123456
#Redis集群不支持多个数据库的概念,默认只有一个数据库,即db 0,所以这里是没有database这个参数的
3-1:方法二:自定义yml配置
yml
复制代码
spring:
redis:
# redisson配置
redisson:
# 如果该值为false,系统将不会创建RedissionClient的bean。
enabled: true
# mode的可用值为,single/cluster/sentinel/master-slave
mode: single
# single: 单机模式
# address: redis://localhost:6379
# cluster: 集群模式
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# sentinel:
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# master-slave:
# 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。
# address: redis://localhost:16379,redis://localhost:26379
address: redis://127.0.0.1:6379
# redis 密码,空可以不填。
password: 123456
database: 0
3-2:RedissonConfig自定义配置类
java
复制代码
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Redisson配置类。
*/
@Configuration
@ConditionalOnProperty(name = "spring.redis.redisson.enabled", havingValue = "true")
public class RedissonConfig {
@Value("${spring.redis.redisson.mode}")
private String mode;
/**
* 仅仅用于sentinel模式。
*/
@Value("${spring.redis.redisson.masterName:}")
private String masterName;
@Value("${spring.redis.redisson.address}")
private String address;
@Value("${spring.redis.redisson.password:}")
private String password;
/**
* 数据库默认0
*/
@Value("${spring.redis.redisson.database:0}")
private Integer database;
@Bean
public RedissonClient redissonClient() {
if (StringUtils.isBlank(password)) {
password = null;
}
Config config = new Config();
if ("single".equals(mode)) {
config.useSingleServer()
.setDatabase(database)
.setPassword(password)
.setAddress(address);
} else if ("cluster".equals(mode)) {
String[] clusterAddresses = address.split(",");
config.useClusterServers()
//集群模式不支持多个数据库概念,默认db 0
.setPassword(password)
.addNodeAddress(clusterAddresses);
} else if ("sentinel".equals(mode)) {
String[] sentinelAddresses = address.split(",");
config.useSentinelServers()
.setDatabase(database)
.setPassword(password)
.setMasterName(masterName)
.addSentinelAddress(sentinelAddresses);
} else if ("master-slave".equals(mode)) {
String[] masterSlaveAddresses = address.split(",");
if (masterSlaveAddresses.length == 1) {
throw new IllegalArgumentException(
"redis.redisson.address MUST have multiple redis addresses for master-slave mode.");
}
String[] slaveAddresses = new String[masterSlaveAddresses.length - 1];
System.arraycopy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
config.useMasterSlaveServers()
.setDatabase(database)
.setPassword(password)
.setMasterAddress(masterSlaveAddresses[0])
.addSlaveAddress(slaveAddresses);
} else {
throw new IllegalArgumentException(mode);
}
return Redisson.create(config);
}
}
4:demo
java
复制代码
@Resource
private RedissonClient redissonClient;
private static final String LOCK_KEY = "myLock";
@SneakyThrows
@GetMapping("test")
public void test() {
// 获取锁对象
RLock lock = redissonClient.getLock(LOCK_KEY);
// 模拟多个线程尝试获取锁
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
// 尝试获取锁,最多等待10秒,获取锁后10秒自动释放
lock.lock(10, TimeUnit.SECONDS);
System.out.println(new Date()+Thread.currentThread().getName() + " 获取到锁,开始处理任务...");
Thread.sleep(2000); // 模拟处理任务需要花费一些时间
System.out.println(new Date()+Thread.currentThread().getName() + " 处理任务完成,释放锁...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 无论如何,最后都要确保锁被释放
lock.unlock();
}
}).start();
}
// 让主线程等待一段时间,以便观察其他线程的行为
Thread.sleep(10000);
System.out.println("===");
}
4-1:运行结果
5-1:自定义注解分布式锁JLock
java
复制代码
package com.huan.study.mybatis.config;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface JLock {
LockModel lockModel() default LockModel.AUTO;
String[] lockKey() default {};
String keyConstant() default "";
long expireSeconds() default 30000L;
long waitTime() default 10000L;
String failMsg() default "获取锁失败,请稍后重试";
}
5-2:LockModel
java
复制代码
package com.huan.study.mybatis.config;
/**
* 锁的模式
*/
public enum LockModel {
//可重入锁
REENTRANT,
//公平锁
FAIR,
//联锁(可以把一组锁当作一个锁来加锁和释放)
MULTIPLE,
//红锁
REDLOCK,
//读锁
READ,
//写锁
WRITE,
//自动模式,当参数只有一个.使用 REENTRANT 参数多个 REDLOCK
AUTO
}
5-3:BaseAspect
java
复制代码
package com.huan.study.mybatis.aspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class BaseAspect {
/**
* 通过spring SpEL 获取参数
*
* @param key 定义的key值 以#开头 例如:#user
* @param parameterNames 形参
* @param values 形参值
* @param keyConstant key的常亮
* @return
*/
public List<String> getValueBySpEL(String key, String[] parameterNames, Object[] values, String keyConstant) {
List<String> keys = new ArrayList<>();
if (!key.contains("#")) {
String s = "redis:lock:" + key + keyConstant;
log.debug("lockKey:" + s);
keys.add(s);
return keys;
}
//spel解析器
ExpressionParser parser = new SpelExpressionParser();
//spel上下文
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], values[i]);
}
Expression expression = parser.parseExpression(key);
Object value = expression.getValue(context);
if (value != null) {
if (value instanceof List) {
List value1 = (List) value;
for (Object o : value1) {
addKeys(keys, o, keyConstant);
}
} else if (value.getClass().isArray()) {
Object[] obj = (Object[]) value;
for (Object o : obj) {
addKeys(keys, o, keyConstant);
}
} else {
addKeys(keys, value, keyConstant);
}
}
log.info("表达式key={},value={}", key, keys);
return keys;
}
private void addKeys(List<String> keys, Object o, String keyConstant) {
keys.add("redis:lock:" + o.toString() + keyConstant);
}
}
5-4:DistributedLockHandler
java
复制代码
package com.huan.study.mybatis.aspect;
import com.huan.study.mybatis.config.JLock;
import com.huan.study.mybatis.config.LockModel;
import lombok.SneakyThrows;
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.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁解析器
*/
@Slf4j
@Aspect
@Component
public class DistributedLockHandler extends BaseAspect {
@Autowired(required = false)
private RedissonClient redissonClient;
/**
* 切面环绕通知
*
* @param joinPoint
* @param jLock
* @return Object
*/
@SneakyThrows
@Around("@annotation(jLock)")
public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {
Object obj = null;
log.info("进入RedisLock环绕通知...");
RLock rLock = getLock(joinPoint, jLock);
boolean res = false;
//获取超时时间
long expireSeconds = jLock.expireSeconds();
//等待多久,n秒内获取不到锁,则直接返回
long waitTime = jLock.waitTime();
//执行aop
if (rLock != null) {
try {
if (waitTime == -1) {
res = true;
//一直等待加锁
rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);
} else {
res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);
}
if (res) {
obj = joinPoint.proceed();
} else {
log.error("获取锁异常");
}
} finally {
if (res) {
rLock.unlock();
}
}
}
log.info("结束RedisLock环绕通知...");
return obj;
}
@SneakyThrows
private RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {
String[] keys = jLock.lockKey();
if (keys.length == 0) {
throw new RuntimeException("keys不能为空");
}
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
Object[] args = joinPoint.getArgs();
LockModel lockModel = jLock.lockModel();
RLock rLock = null;
String keyConstant = jLock.keyConstant();
if (lockModel.equals(LockModel.AUTO)) {
if (keys.length > 1) {
lockModel = LockModel.REDLOCK;
} else {
lockModel = LockModel.REENTRANT;
}
}
if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
}
switch (lockModel) {
case FAIR:
rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));
break;
case REDLOCK:
List<RLock> rLocks = new ArrayList<>();
for (String key : keys) {
List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
for (String s : valueBySpEL) {
rLocks.add(redissonClient.getLock(s));
}
}
RLock[] locks = new RLock[rLocks.size()];
int index = 0;
for (RLock r : rLocks) {
locks[index++] = r;
}
rLock = new RedissonRedLock(locks);
break;
case MULTIPLE:
rLocks = new ArrayList<>();
for (String key : keys) {
List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
for (String s : valueBySpEL) {
rLocks.add(redissonClient.getLock(s));
}
}
locks = new RLock[rLocks.size()];
index = 0;
for (RLock r : rLocks) {
locks[index++] = r;
}
rLock = new RedissonMultiLock(locks);
break;
case REENTRANT:
List<String> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);
//如果spel表达式是数组或者LIST 则使用红锁
if (valueBySpEL.size() == 1) {
rLock = redissonClient.getLock(valueBySpEL.get(0));
} else {
locks = new RLock[valueBySpEL.size()];
index = 0;
for (String s : valueBySpEL) {
locks[index++] = redissonClient.getLock(s);
}
rLock = new RedissonRedLock(locks);
}
break;
case READ:
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();
break;
case WRITE:
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();
break;
}
return rLock;
}
}