eladmin 整合 redis 多数据源
yml
redis:
lettuce:
pool:
# 连接超时
connTimeout: 10000
# 连接池中的最大空闲连接
maxIdle: 100
# 连接池中的最小空闲连接 默认为 0
minIdle: 1
# 连接池最大连接数(使用负值表示没有限制)
maxActive: 200
# 连接池最大阻塞等待时间
maxWait: 2
redis-system:
database: ${REDIS_DB:0}
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PWD:ahernlee}
redis-mall:
database: ${REDIS_DB:1}
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PWD:ahernlee}
java代码
/*
* Copyright 2020-2021 Li Ya Heng
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pers.liyaheng.config.redis;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
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 pers.liyaheng.utils.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* @author Li Ya Heng
* @date 2020-11-24
*/
@Slf4j
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* 配置公用lettuce连接池
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
public GenericObjectPoolConfig redisPool() {
return new GenericObjectPoolConfig();
}
/**
*
*
*
* 配置system后台redis连接
*
*
*/
// 配置system后台默认defaultFactory数据源连接,这里注意:需要添加@Primary 指定bean的名称,目的是为了创建两个不同名称的LettuceConnectionFactory
@Bean("systemFactory")
@Primary
public LettuceConnectionFactory systemFactory(@Value("${spring.redis-system.host}") String host,
@Value("${spring.redis-system.port}") int port,
@Value("${spring.redis-system.password}") String password,
@Value("${spring.redis-system.database}") int db,
GenericObjectPoolConfig config) {
RedisStandaloneConfiguration redisSystemConfig = new RedisStandaloneConfiguration(host, port);
redisSystemConfig.setDatabase(db);
redisSystemConfig.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisSystemConfig, clientConfiguration);
return factory;
}
//配置system数据源的RedisTemplate,注意:这里指定使用名称=factory 的 RedisConnectionFactory,并且标识第一个数据源是默认数据源 @Primary
@Bean("systemRedisTemplate")
@Primary
public RedisTemplate<Object, Object> systemRedisTemplate(@Qualifier("systemFactory") RedisConnectionFactory systemFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//序列化
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType,这里方便开发,使用全局的方式
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
// ParserConfig.getGlobalInstance().addAccept("pers.liyaheng.domain");
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(systemFactory);
return template;
}
//配置system数据源的RedisCacheManager,注意:这里指定使用名称systemCacheManager,并且标识默认 @Primary
@Bean("systemCacheManager")
@Primary
public RedisCacheManager systemCacheManager(@Qualifier("systemFactory") RedisConnectionFactory systemFactory) {
//设置默认过期时间为6小时,设置@cacheable 序列化方式
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.
SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6));
RedisCacheManager redisCacheManager = RedisCacheManager.builder(systemFactory)
.cacheDefaults(configuration)
.transactionAware()
.build();
log.debug("自定义systemCacheManager加载完成");
return redisCacheManager;
}
/**
*
*
*
* 配置mall微商城redis连接
*
*
*/
// 配置mall数据源的连接工厂
@Bean("mallFactory")
public LettuceConnectionFactory mallFactory(@Value("${spring.redis-mall.host}") String host,
@Value("${spring.redis-mall.port}") int port,
@Value("${spring.redis-mall.password}") String password,
@Value("${spring.redis-mall.database}") int db,
GenericObjectPoolConfig config) {
RedisStandaloneConfiguration redisMallConfig = new RedisStandaloneConfiguration(host, port);
redisMallConfig.setDatabase(db);
redisMallConfig.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisMallConfig, clientConfiguration);
return factory;
}
//配置mall数据源的RedisTemplate
@Bean("mallRedisTemplate")
public RedisTemplate<Object, Object> mallRedisTemplate(@Qualifier("mallFactory") RedisConnectionFactory mallFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//序列化
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType,这里方便开发,使用全局的方式
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
// ParserConfig.getGlobalInstance().addAccept("pers.liyaheng.domain");
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(mallFactory);
return template;
}
//配置mall数据源的RedisCacheManager
@Bean("mallCacheManager")
public RedisCacheManager mallCacheManager(@Qualifier("mallFactory") RedisConnectionFactory mallFactory) {
//设置默认过期时间为6小时,设置@cacheable 序列化方式
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.
SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6));
RedisCacheManager redisCacheManager = RedisCacheManager.builder(mallFactory)
.cacheDefaults(configuration)
.transactionAware()
.build();
log.debug("自定义mallCacheManager加载完成");
return redisCacheManager;
}
/**
* 默认将使用该策略
*/
@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);
}
};
}
}
/**
* Value 序列化
*
* @author /
* @param <T>
*/
class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private final Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
}
@Override
public T deserialize(byte[] bytes) {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, StandardCharsets.UTF_8);
return JSON.parseObject(str, clazz);
}
}
/**
* 重写序列化器
*
* @author /
*/
class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
StringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
private StringRedisSerializer(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) {
String string = JSON.toJSONString(object);
if (StringUtils.isBlank(string)) {
return null;
}
string = string.replace("\"", "");
return string.getBytes(charset);
}
}

浙公网安备 33010602011771号