1、pom依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-20</artifactId>
<version>3.10.2</version>
</dependency>
<!--End redis -->2、yml配置
spring:
cache:
type: redis
redis:
# redis库
database: 0
# #方式一: 集群
# cluster:
# nodes: 192.168.56.99:6379,192.168.59.100:6379
#方式二: 单机 二选一
port: 6379
host: 127.0.0.1
# redis 密码
password:
# 连接超时时间(毫秒)
timeout: 1000
jedis:
pool:
# 连接池最大链接数(负数表示没有限制)
max-active: 8
# 连接池最大阻塞等待时间(负数表示没有限制)
max-wait: 3000
# 连接池最大空闲连接数
max-idle: 8
# 连接池最小空闲连接数
min-idle: 03、config配置
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.time.Duration;
@Configuration
@EnableCaching
@Slf4j
@AllArgsConstructor
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private final RedisProperties redisProperties;
/**
* 配置cacheManager
*
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// redisCacheManager构造器需要提供一个redisCacheWriter和一个redisCacheConfigurer
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
// 配置cache 序列化为jsonSerializer
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration
.defaultCacheConfig()
.prefixCacheNameWith("cache:")
.serializeValuesWith(pair)
.entryTtl(Duration.ofDays(1));// 设置默认过期时间一天
// .computePrefixWith(cacheName -> "caching:" + cacheName);
// 也可以通过builder来构建
// RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(defaultCacheConfig).transactionAware().build();
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
//单机
config.useSingleServer()
.setAddress("redis://" + redisProperties.getHost()+":"+redisProperties.getPort())
;
// //集群
// //redis服务器地址,多个逗号分隔
// RedisProperties.Cluster cluster = redisProperties.getCluster();
// List<String> nodes = cluster.getNodes();
// Set<String> nodeSet = new HashSet<>();
// for (String node : nodes) {
// nodeSet.add("redis://" + node);
// }
// config.useClusterServers()
// .setScanInterval(2000)
// .addNodeAddress(nodeSet.toArray(new String[nodeSet.size()]))
// ;
return Redisson.create(config);
}
/**
* @param redisConnectionFactory:配置不同的客户端,这里注入的redis连接工厂不同: JedisConnectionFactory、LettuceConnectionFactory
* @功能描述 :配置Redis序列化,原因如下:
* (1) StringRedisTemplate的序列化方式为字符串序列化,
* RedisTemplate的序列化方式默为jdk序列化(实现Serializable接口)
* (2) RedisTemplate的jdk序列化方式在Redis的客户端中为乱码,不方便查看,
* 因此一般修改RedisTemplate的序列化为方式为JSON方式【建议使用GenericJackson2JsonRedisSerializer】
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// key采用String的序列化方式
redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
// value序列化方式采用jackson
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8);
//hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
/**
* 设置自动key的生成规则,配置spring boot的注解,进行方法级别的缓存
* 使用:进行分割,可以很多显示出层级关系
* 这里其实就是new了一个KeyGenerator对象,只是这是lambda表达式的写法,我感觉很好用,大家感兴趣可以去了解下
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(":");
sb.append(method.getName());
for (Object obj : params) {
sb.append(":" + String.valueOf(obj));
}
String rsToUse = String.valueOf(sb);
log.info("自动生成Redis Key -> [{}]", rsToUse);
return rsToUse;
};
}
/**
* 异常处理,当Redis发生异常时,打印日志,但是程序正常走
* @return
*/
@Bean
public CacheErrorHandler errorHandler() {
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
log.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
}
4、redis锁工具类
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/***
* redis分布式锁工具类
*/
@Component
public class RedissonUtil {
private static final Logger logger = LoggerFactory.getLogger(RedissonUtil.class);
/**
* redis锁前缀
*/
public static final String SYS_LOCK_FLAG = "lock";
/**
* 用于隔开缓存前缀与缓存键值
*/
public static final String KEY_SPLIT = ":";
// 静态属性注入
private static RedissonClient redissonClient;
@Autowired
public void setRedisson(RedissonClient redissonClient) {
RedissonUtil.redissonClient = redissonClient;
}
/**
* 加锁
* @param lockName 锁名 相同的key表示相同的锁,建议针对不同的业务使用不同的key
* @param expiresTime 过期时间,单位:秒
* @param waitTime 最大等待时间时间,单位:秒
* @return
*/
public static boolean getLock(String lockName, long expiresTime,long waitTime) {
//最长等待时间
String key = getLockKey(lockName);
//获取锁对象
RLock lock = redissonClient.getLock(key);
//设置锁过期时间,防止死锁的产生
boolean flag = true;
try {
flag = lock.tryLock(waitTime,expiresTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("获取锁成功,Redis Lock key: {}", key);
return flag;
}
/**
* 释放锁,建议放在 finally里面
* @param lockName 锁名称
*/
public static void unlock(String lockName) {
String key = getLockKey(lockName);
//获取所对象
RLock lock = redissonClient.getLock(key);
// 释放锁,判断要解锁的key是否已被锁定并且是否被当前线程保持
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
logger.info("释放Redis锁成功,key:{}", key);
}
}
/**
* 对锁的key添加系统标识前缀
* @return
*/
private static String getLockKey(String key) {
return RedissonUtil.SYS_LOCK_FLAG + RedissonUtil.KEY_SPLIT + key;
}
}
5、缓存及锁示例
@Service
public class UserDemoService {
/**
* 缓存示例:第一次调用记录缓存
* @param userName
* @return
*/
@Cacheable(cacheNames = "users",key = "#userName")
public UserDemo getByUserName(String userName){
UserDemo userDemo = new UserDemo();
userDemo.setUserName("zhangsan");
userDemo.setPwd("123456");
System.out.println("getByUserName:"+userDemo.toString());
return userDemo;
}
/**
* 缓存示例:更新缓存
* @param userName
* @param pwd
* @return
*/
@CachePut(cacheNames = "users",key = "#userName")
public UserDemo updateUser(String userName, String pwd){
UserDemo userDemo = new UserDemo();
userDemo.setUserName(userName);
userDemo.setPwd(pwd);
System.out.println("updateUser:"+userDemo.toString());
return userDemo;
}
/**
* 缓存示例:删除缓存
* @param userName
*/
@CacheEvict(cacheNames = "users",key = "#userName")
public void delUser(String userName) {
System.out.println("delUser:"+userName);
}
/**
* 缓存示例:
* 查询全部用户
* @return
*/
@Cacheable(cacheNames = "users", key = "'allUsers'", unless = "#result==null")
public List<UserDemo> allUsers() {
System.out.println("allUsers:");
List<UserDemo> list = new ArrayList<>();
return list;
}
/**
* 分布式锁示例
*/
public void test(){
System.out.println("========开始=====");
if(RedissonUtil.getLock("mylock",5L,3L)){
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
RedissonUtil.unlock("mylock");
System.out.println("解锁:"+System.currentTimeMillis());
}else {
System.out.println("未获得锁:"+System.currentTimeMillis());
}
}
}
版权声明:本文为yancaobisi原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。