引入了Redisson
后不需要再额外引入 spring-boot-starter-data-redis
,
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.6</version>
</dependency>
由于还有加锁并加事务执行的场景,所以我这里先新建一个事务工具类TransactionUtil.java
,内容如下:
package com.zyq.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
/** 事务工具
* author xiaochi
* date 2024/8/22
*/
@Slf4j
@Component
public class TransactionUtil {
private static TransactionTemplate transactionTemplate;
@Autowired
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
TransactionUtil.transactionTemplate = transactionTemplate;
}
/**
* 事务执行
* @param callback
* @return
*/
public static boolean execute(Fn<Boolean> callback){
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate.setTimeout(10);
transactionTemplate.setReadOnly(false);
Boolean rs = transactionTemplate.execute(status -> {
try {
return callback.run();
} catch (Exception e) {
status.setRollbackOnly();
log.info("事务异常:{}", e);
return false;
}
});
return rs != null && rs;
}
@FunctionalInterface
public interface Fn<T>{
T run();
}
}
接着Redisson
配置类创建RedisConfig.java
package com.demo2.config;
import cn.hutool.json.JSONUtil;
import io.lettuce.core.ClientOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
/**
* RedisConfig
* @author xiaochi
* @date 2021/12/11 22:13
*/
@Slf4j
@Configuration
public class RedisConfig {
@Value("${spring.redis.database}")
private Integer database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private Long timeout;
@Value("${spring.redis.lettuce.pool.max-active}")
private Integer maxActive;
@Value("${spring.redis.lettuce.pool.max-wait}")
private Long maxWait;
@Value("${spring.redis.lettuce.pool.max-idle}")
private Integer maxIdle;
@Value("${spring.redis.lettuce.pool.min-idle}")
private Integer minIdle;
/**
* Redisson 配置
* @return
*/
@Bean(destroyMethod="shutdown")
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setPassword(password)
.setDatabase(database)
.setConnectionPoolSize(maxActive) // 连接池大小,默认64
.setConnectionMinimumIdleSize(minIdle) // 最小空闲连接数,默认32
.setRetryAttempts(3) // 命令失败重试次数 3
.setRetryInterval(1500) // 命令重试发送时间间隔(毫秒) 默认1500
.setTimeout(timeout.intValue()) // 命令等待超时(毫秒) 默认10000
.setConnectTimeout(timeout.intValue()) // 连接空闲超时(毫秒) 默认10000
.setIdleConnectionTimeout(timeout.intValue()) // 连接空闲超时(毫秒) 默认10000
.setSubscriptionConnectionMinimumIdleSize(minIdle) // 发布和订阅连接的最小空闲连接数
.setSubscriptionConnectionPoolSize(maxActive) // 发布和订阅连接池大小 默认50
.setDnsMonitoringInterval(timeout) // DNS监测时间间隔(毫秒),默认5000*//*
.setAddress("redis://"+host+":"+port);
//config.setThreads(Runtime.getRuntime().availableProcessors());// 默认 16
return Redisson.create(config);
}
/**
* Redis 序列化策略
* @param lettuceConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
JsonStringRedisSerializer jsonStringRedisSerializer= new JsonStringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jsonStringRedisSerializer);
redisTemplate.setValueSerializer(jsonStringRedisSerializer);
log.info("Redis init succuss.");
return redisTemplate;
}
private static class JsonStringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
public JsonStringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
public JsonStringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
if (object == null) {
return new byte[0];
}
if(object instanceof String){
return object.toString().getBytes(charset);
}else {
String string = JSONUtil.toJsonStr(object);
return string.getBytes(charset);
}
}
}
}
到此配置完成,封装一个Redisson分布式锁的工具LockUtil.java
package com.zyq.util;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/** 加锁工具
* author xiaochi
* date 2024/8/2
*/
@Slf4j
@Component
public class LockUtil {
private static long WAITTIME = 10;// 等待时间
private static long LOCKRUNTIME = 5;// 持有锁时间
private static final byte[] lock = new byte[0];
private static String lockKey = "API-LOCK";
private static RedissonClient redissonClient;
@Autowired
public void setRedissonClient(RedissonClient redissonClient) {
LockUtil.redissonClient = redissonClient;
log.info("LockUtil init finish.");
}
public static RedissonClient getRedissonClient() {
return redissonClient;
}
/**
* synchronized 加锁执行
*/
public static void runSynchronized(Runnable runnable){
synchronized (lock){
runnable.run();
}
}
/**
* redission 加锁执行
*/
public static RedissonClient run(Runnable runnable){
return run(lockKey,runnable);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param runnable runnable
* @return
*/
public static RedissonClient run(String lockKey, Runnable runnable){
return run(lockKey,WAITTIME,LOCKRUNTIME,TimeUnit.SECONDS,runnable);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param lockRunTime 加锁时间
* @param runnable runnable
* @return
*/
public static RedissonClient run(String lockKey,long lockRunTime, Runnable runnable){
return run(lockKey,WAITTIME,lockRunTime,TimeUnit.SECONDS,runnable);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param waitGetLockTime 获取锁的等待时间
* @param lockRunTime 加锁时间
* @param runnable runnable
* @return
*/
public static RedissonClient run(String lockKey,long waitGetLockTime,long lockRunTime, Runnable runnable){
return run(lockKey,waitGetLockTime,lockRunTime,TimeUnit.SECONDS,runnable);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param waitGetLockTime 获取锁的等待时间
* @param lockRunTime 加锁时间
* @param timeUnit 时间单位
* @param runnable runnable
* @return
*/
public static RedissonClient run(String lockKey,long waitGetLockTime,long lockRunTime, TimeUnit timeUnit,Runnable runnable){
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(waitGetLockTime,lockRunTime, timeUnit)){
runnable.run();
}
}catch (Exception e){
// log.info("LockUtil Runnable执行异常,{}",e);此处抛出错误,是为了全局捕获异常提示信息返回前端
throw new IllegalArgumentException(e.getMessage());
}finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()){
lock.unlock();
}
}
return redissonClient;
}
//================================= boolean返回值 =============================
/**
* redission 加锁执行
*/
public static boolean run(Supplier<Boolean> supplier){
return run(lockKey,supplier);
}
/**
* 加锁加事务执行
* @param supplier
*/
public static boolean runTransaction(Supplier<Boolean> supplier){
return run(lockKey,() -> TransactionUtil.execute(() -> {
try {
return supplier.get();
}catch (Exception e){
log.info("LockUtil加锁事务执行异常,{}",e);
return false;
}
}));
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param supplier supplier
* @return
*/
public static boolean run(String lockKey, Supplier<Boolean> supplier){
return run(lockKey,WAITTIME,LOCKRUNTIME,TimeUnit.SECONDS,supplier);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param lockRunTime 加锁时间
* @param supplier supplier
* @return
*/
public static boolean run(String lockKey,long lockRunTime, Supplier<Boolean> supplier){
return run(lockKey,WAITTIME,lockRunTime,TimeUnit.SECONDS,supplier);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param waitGetLockTime 获取锁的等待时间
* @param lockRunTime 加锁时间
* @param supplier run
* @return
*/
public static boolean run(String lockKey,long waitGetLockTime,long lockRunTime, Supplier<Boolean> supplier){
return run(lockKey,waitGetLockTime,lockRunTime,TimeUnit.SECONDS,supplier);
}
/**
* Redis分布式加锁执行
* @param lockKey 锁key
* @param waitGetLockTime 获取锁的等待时间
* @param lockRunTime 加锁时间
* @param timeUnit 时间单位
* @param supplier supplier
* @return
*/
public static boolean run(String lockKey,long waitGetLockTime,long lockRunTime, TimeUnit timeUnit,Supplier<Boolean> supplier){
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(waitGetLockTime,lockRunTime, timeUnit)){
return supplier.get();
}
}catch (Exception e){
// log.info("LockUtil Supplier执行异常,{}",e); 此处抛出错误,是为了全局捕获异常提示信息返回前端
throw new IllegalArgumentException(e.getMessage());
}finally {
if (lock.isHeldByCurrentThread() && lock.isLocked()){
lock.unlock();
}
}
return false;
}
}
使用如下:
private static int count = 0;
public static void main(String[] args) throws Exception{
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
LockUtil.run("lockkey",() -> {
try {
count++;
System.out.println("Count: " + count);
} catch (Exception e) {
e.printStackTrace();
}
});
});
}
executor.shutdown();
}
到此完成。