在实际开发中,我们可能会用到2个不同的redis数据源;如何连接查询详情:
文章目录
- 一、依赖
- 二、配置文件
- 三、config类配置
- 四、序列化问题
- 五、封装工具类
一、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
二、配置文件
spring:
redis:
lettuce:
pool:
connTimeout: 10000
maxIdle: 100
minIdle: 1
maxActive: 200
maxWait: 2
redis-data:
host: ip1
port: 6679
password: 123456
db: 0
redis-concurrency:
host: ip2
port: 6779
password: 1111
三、config类配置
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* 配置lettuce连接池
*
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
public GenericObjectPoolConfig redisPool() {
return new GenericObjectPoolConfig<>();
}
/**
* 配置第一个数据源的连接工厂
* 这里注意:需要添加@Primary 指定bean的名称,目的是为了创建两个不同名称的LettuceConnectionFactory
*
* @return
*/
@Bean("dataFactory")
@Primary
public LettuceConnectionFactory dataFactory(@Value("${spring.redis-data.host}") String host,
@Value("${spring.redis-data.port}") int port,
@Value("${spring.redis-data.password}") String password,
@Value("${spring.redis-data.db}") int db,
GenericObjectPoolConfig config) {
RedisStandaloneConfiguration redisDataConfig = new RedisStandaloneConfiguration(host, port);
redisDataConfig.setDatabase(db);
redisDataConfig.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisDataConfig, clientConfiguration);
return factory;
}
//第二个数据源的连接工厂
@Bean("concurrencyFactory")
public LettuceConnectionFactory concurrencyFactory(@Value("${spring.redis-concurrency.host}") String host,
@Value("${spring.redis-concurrency.port}") int port,
@Value("${spring.redis-concurrency.password}") String password,
GenericObjectPoolConfig config) {
RedisStandaloneConfiguration redisDataConfig = new RedisStandaloneConfiguration(host, port);
redisDataConfig.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisDataConfig, clientConfiguration);
return factory;
}
/**
* 配置第一个数据源的RedisTemplate
* 注意:这里指定使用名称=factory 的 RedisConnectionFactory
* 并且标识第一个数据源是默认数据源 @Primary
*
* @param dataFactory
* @return
*/
@Bean("redisDataTemplate")
@Primary
public RedisTemplate<String, Object> redisDataTemplate(@Qualifier("dataFactory") RedisConnectionFactory dataFactory) {
return getRedisDataTemplate(dataFactory);
}
//第二个数据源
@Bean("redisConcurrencyTemplate")
public RedisTemplate<String, Object> redisConcurrencyTemplate(@Qualifier("concurrencyFactory") RedisConnectionFactory concurrencyFactory) {
return getRedisConcurrencyTemplate(concurrencyFactory);
}
/**
* 设置序列化方式 (这一步不是必须的)
*
* @return
*/
private RedisTemplate<String, Object> getRedisDataTemplate(RedisConnectionFactory factory) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//redis 的key 带""号,如"key"
template.setKeySerializer(jackson2JsonRedisSerializer);
//redis 的value 不带""号,如"value"
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public RedisTemplate<String, Object> getRedisConcurrencyTemplate(RedisConnectionFactory redisConnectionFactory) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//redis 的key 不带""号,如key
template.setKeySerializer(new StringRedisSerializer());
//redis 的value 不带""号,如value
template.setValueSerializer(new StringRedisSerializer());
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
四、序列化问题
在连接多个redis数据源时,因为我需要取由他人存储的数据,遇到了key和value序列化的问题;
如果序列号方式不对,是取不到数据的。
如果没有该问题getRedisDataTemplate和getRedisConcurrencyTemplate可以合成一个公共方法;
如果遇到这个坑,查看getRedisDataTemplate和getRedisConcurrencyTemplate方法中序列化方式的不同即可。
getRedisDataTemplate在redis中的key带双引号;value也是带的
getRedisConcurrencyTemplate在rdm中的key不带双引号,value也是不带的;
终端取出key,明显看出其差别:
五、封装工具类
1、SpringContextUtil
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
}
2、RedisLock
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RedisLock {
TRANSFER_TASK("transferTask"), MESSAGE_TASK("messageTask"),
INIT_USERS_TASK("usersTask"), INIT_DEPARTS_TASK("departsTask"), EXTERNALCONTACT_TAG_REMARK("externalcontactTagRemark");
private String taskName;
}
3、封装第一个RedisDataHelper
import com.data.common.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("unchecked")
@Slf4j
public class RedisDataHelper {
private static RedisTemplate<String, Object> redisTemplate = (RedisTemplate) SpringContextUtil.getBean("redisDataTemplate");
static {
//处理因为系统异常,key没有被清除的情况
RedisLock[] values = RedisLock.values();
for (RedisLock redisLock : values)
redisTemplate.delete(redisLock.getTaskName());
}
public static Boolean hasKey(String redisKey) {
return redisTemplate.hasKey(redisKey);
}
public static <T> T get(String redisKey) {
return (T) redisTemplate.opsForValue().get(redisKey);
}
public static void set(String redisKey, Object value, Long minutesTimeout) {
redisTemplate.opsForValue().set(redisKey, value, minutesTimeout, TimeUnit.MINUTES);
}
public static void set(String redisKey, Object value) {
redisTemplate.opsForValue().set(redisKey, value);
}
public static void set(String redisKey, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(redisKey, value, timeout, unit);
}
public static Boolean delete(String redisKey) {
return redisTemplate.delete(redisKey);
}
public static int deleteKeys(String redisKey) {
Set<String> keys = redisTemplate.keys(redisKey + "*");
for (String o : keys) {
redisTemplate.delete(o);
}
return keys.size();
}
public static <T> T get(String redisKey, Long minutesTimeout, ICallable<T> task) {
long beginTime = System.currentTimeMillis();
T obj;
if (redisTemplate.hasKey(redisKey)) {
log.info("RedisHelper get redisKey:[{}] times:[{}]ms", redisKey, System.currentTimeMillis() - beginTime);
obj = (T) redisTemplate.opsForValue().get(redisKey);
} else {
obj = task.call();
redisTemplate.opsForValue().set(redisKey, obj, minutesTimeout, TimeUnit.MINUTES);
log.info("RedisHelper setRedisKey:[{}] times:[{}]ms", redisKey, System.currentTimeMillis() - beginTime);
}
return obj;
}
public static void executeWithLock(RedisLock redisLock, Runnable task) {
String lockKey = redisLock.getTaskName();
String value = "";
Boolean isAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, value);
if (isAbsent) {
log.info("{} begin 。。。", lockKey);
long t1 = System.nanoTime();
try {
task.run();
} catch (Exception e) {
log.error(lockKey, e);
} finally {
long t2 = System.nanoTime();
final double ms = (t2 - t1) / 1e6d;
log.info(String.format("%s finished in %.1fms", lockKey, ms));
//小于10s,做延迟
if (ms < 10_000) {
redisTemplate.opsForValue().set(lockKey, value, 10, TimeUnit.SECONDS);
log.info("redisLock {} delay 10s", lockKey);
} else {
redisTemplate.delete(lockKey);
log.info("redisLock {} is deleted", lockKey);
}
}
}
}
@FunctionalInterface
public interface ICallable<V> {
V call();
}
}
4、封装第二个RedisConcurrencyHelper
import com.data.common.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("unchecked")
@Slf4j
public class RedisConcurrencyHelper {
private static RedisTemplate<String, Object> redisTemplate = (RedisTemplate) SpringContextUtil.getBean("redisConcurrencyTemplate");
static {
//处理因为系统异常,key没有被清除的情况
RedisLock[] values = RedisLock.values();
for (RedisLock redisLock : values)
redisTemplate.delete(redisLock.getTaskName());
}
public static <T> T get(String redisKey) {
return (T) redisTemplate.opsForValue().get(redisKey);
}
public static void set(String redisKey, Object value, Long minutesTimeout) {
redisTemplate.opsForValue().set(redisKey, value, minutesTimeout, TimeUnit.MINUTES);
}
public static void addSet(String key,String value,Double score){
redisTemplate.opsForZSet().add(key,value,score);
}
public static Set gerZset(String key,int offset,int limit){
return redisTemplate.opsForZSet().range(key, offset, limit);
}
public static Set zrangBySocre(String key,double min,double max,int offset,int limit){
return redisTemplate.opsForZSet().rangeByScore(key,min,max,offset,limit);
}
@FunctionalInterface
public interface ICallable<V> {
V call();
}
}