package cn.lili.cache.config.redis;
|
|
import cn.hutool.core.text.CharSequenceUtil;
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.parser.ParserConfig;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.codec.digest.DigestUtils;
|
import org.redisson.Redisson;
|
import org.redisson.api.RedissonClient;
|
import org.redisson.config.ClusterServersConfig;
|
import org.redisson.config.Config;
|
import org.redisson.config.SentinelServersConfig;
|
import org.redisson.config.SingleServerConfig;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.cache.Cache;
|
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
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.context.annotation.Primary;
|
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.connection.lettuce.LettuceConnectionFactory;
|
import org.springframework.data.redis.core.RedisOperations;
|
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|
import java.time.Duration;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 自定义redis配置
|
*
|
* @author Chopper
|
* @version v4.0
|
* @since 2021/3/20 09:37
|
*/
|
|
@Slf4j
|
@Configuration
|
@ConditionalOnClass(RedisOperations.class)
|
@EnableConfigurationProperties(RedisProperties.class)
|
public class RedisConfig extends CachingConfigurerSupport {
|
|
|
private static final String REDIS_PREFIX = "redis://";
|
|
@Value("${lili.cache.timeout:7200}")
|
private Integer timeout;
|
|
/**
|
* 当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器
|
*
|
* @param connectionFactory 链接工厂
|
* @return 缓存
|
*/
|
@Bean
|
@Primary
|
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
|
//初始化一个RedisCacheWriter
|
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
|
//序列化方式2
|
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
|
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
|
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
|
//设置过期时间
|
defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(timeout));
|
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
|
|
//设置白名单---非常重要********
|
/*
|
使用fastjson的时候:序列化时将class信息写入,反解析的时候,
|
fastjson默认情况下会开启autoType的检查,相当于一个白名单检查,
|
如果序列化信息中的类路径不在autoType中,
|
反解析就会报com.alibaba.fastjson.JSONException: autoType is not support的异常
|
可参考 https://blog.csdn.net/u012240455/article/details/80538540
|
*/
|
ParserConfig.getGlobalInstance().addAccept("cn.lili.");
|
ParserConfig.getGlobalInstance().addAccept("cn.hutool.json.");
|
|
return cacheManager;
|
}
|
|
@Bean(name = "redisTemplate")
|
@ConditionalOnMissingBean(name = "redisTemplate")
|
public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
|
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
//使用fastjson序列化
|
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
|
//value值的序列化采用fastJsonRedisSerializer
|
template.setValueSerializer(fastJsonRedisSerializer);
|
template.setHashValueSerializer(fastJsonRedisSerializer);
|
//key的序列化采用StringRedisSerializer
|
template.setKeySerializer(new StringRedisSerializer());
|
template.setHashKeySerializer(new StringRedisSerializer());
|
template.setConnectionFactory(lettuceConnectionFactory);
|
return template;
|
}
|
|
@Bean(destroyMethod = "shutdown")
|
public RedissonClient redisson(RedisProperties redisProperties) {
|
Config config = new Config();
|
if (redisProperties.getSentinel() != null && !redisProperties.getSentinel().getNodes().isEmpty()) {
|
// 哨兵模式
|
SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
|
sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster());
|
List<String> sentinelAddress = new ArrayList<>();
|
for (String node : redisProperties.getCluster().getNodes()) {
|
sentinelAddress.add(REDIS_PREFIX + node);
|
}
|
sentinelServersConfig.setSentinelAddresses(sentinelAddress);
|
if (CharSequenceUtil.isNotEmpty(redisProperties.getSentinel().getPassword())) {
|
sentinelServersConfig.setSentinelPassword(redisProperties.getSentinel().getPassword());
|
}
|
} else if (redisProperties.getCluster() != null && !redisProperties.getCluster().getNodes().isEmpty()) {
|
// 集群模式
|
ClusterServersConfig clusterServersConfig = config.useClusterServers();
|
List<String> clusterNodes = new ArrayList<>();
|
for (String node : redisProperties.getCluster().getNodes()) {
|
clusterNodes.add(REDIS_PREFIX + node);
|
}
|
clusterServersConfig.setNodeAddresses(clusterNodes);
|
if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) {
|
clusterServersConfig.setPassword(redisProperties.getPassword());
|
}
|
} else {
|
SingleServerConfig singleServerConfig = config.useSingleServer();
|
singleServerConfig.setAddress(REDIS_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort());
|
if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) {
|
singleServerConfig.setPassword(redisProperties.getPassword());
|
}
|
singleServerConfig.setPingConnectionInterval(1000);
|
}
|
|
return Redisson.create(config);
|
}
|
|
/**
|
* 自定义缓存key生成策略,默认将使用该策略
|
*/
|
@Bean
|
@Override
|
public KeyGenerator keyGenerator() {
|
return (target, method, params) -> {
|
Map<String, Object> container = new HashMap<>(3);
|
Class<?> targetClassClass = target.getClass();
|
//类地址
|
container.put("class", targetClassClass.toGenericString());
|
//方法名称
|
container.put("methodName", method.getName());
|
//包名称
|
container.put("package", targetClassClass.getPackage());
|
//参数列表
|
for (int i = 0; i < params.length; i++) {
|
container.put(String.valueOf(i), params[i]);
|
}
|
//转为JSON字符串
|
String jsonString = JSON.toJSONString(container);
|
//做SHA256 Hash计算,得到一个SHA256摘要作为Key
|
return DigestUtils.sha256Hex(jsonString);
|
};
|
}
|
|
@Bean
|
@Override
|
public CacheErrorHandler errorHandler() {
|
//异常处理,当Redis发生异常时,打印日志,但是程序正常走
|
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
|
return 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);
|
}
|
};
|
}
|
|
}
|