西安百度推广网站,网站推荐正能量,顺德区网站设计,网站建站知识目录 前言依赖yml配置redis多集群数据源配置类思考 redis工具类 前言
工作时有一个项目配置了多个redis数据源#xff0c;使用时出现了指定了使用副数据源#xff0c;数据却依然使用了主数据源的情况。经过排查#xff0c;发现配置流程较为繁琐易错#xff0c;此处做一个记… 目录 前言依赖yml配置redis多集群数据源配置类思考 redis工具类 前言
工作时有一个项目配置了多个redis数据源使用时出现了指定了使用副数据源数据却依然使用了主数据源的情况。经过排查发现配置流程较为繁琐易错此处做一个记录。
依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-redis/artifactIdversion1.4.1.RELEASE/version
/dependencyyml配置
此处设置两个不同的redis地址可以debug时在redisTemplate对象中查看此时使用的到底是哪一个数据源方便排查问题。
spring:redis:jedis:pool:maxActive: 1000minIdle: 1maxWait: 5000maxIdle: 5timeout: 6000msredis-one: # 第一个redis主集群配置cluster:node: localhost:6379password: 123456redis-two: # 第二个redis其他集群配置cluster:node: 127.0.0.1:6379password: 123456redis多集群数据源配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
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.core.env.Environment;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;Configuration
EnableCaching
Slf4j
public class RedisDataSourceConfig extends CachingConfigurerSupport {Resourceprivate Environment environment;Resourceprivate RedisProperties redisProperties;/*** 主业务redis操作模板** param factoryOne* return*/Bean(name redisOneTemplate)Primarypublic RedisTemplateString, Object redisOneTemplate(Autowired Qualifier(factoryOne) JedisConnectionFactory factoryOne) {return getRedisTemplate(factoryOne);}/*** 副redis操作模板* 注意初始化配置有误导致实际查询的还是第一个redis(redis-one)配置* param factoryTwo* return*/Bean(name redisTwoTemplate)public RedisTemplateString, Object redisTwoTemplate(Autowired Qualifier(factoryTwo) JedisConnectionFactory factoryTwo) {return getRedisTemplate(factoryTwo);}/*** 副redis操作模板真* 真实指向 redis-two配置* param factoryTwoReal* return*/Bean(name redisTwoRealTemplate)public RedisTemplateString, Object redisTwoRealTemplate(Autowired Qualifier(factoryTwoReal) JedisConnectionFactory factoryTwoReal) {return getRedisTemplate(factoryTwoReal);}/* 集群模式配置 start *//*** 指向redis-one配置*/Bean(factoryOne)Primarypublic JedisConnectionFactory factoryOne(RedisStandaloneConfiguration redisConfigOne, JedisClientConfiguration clientConfig) {return new JedisConnectionFactory(redisConfigOne, clientConfig);}/*** 真实指向redis-one配置*/Bean(factoryTwo)public JedisConnectionFactory factoryTwo(RedisStandaloneConfiguration redisConfigTwo, JedisClientConfiguration clientConfig) {return new JedisConnectionFactory(redisConfigTwo, clientConfig);}/*** 真实指向redis-two配置*/Bean(factoryTwoReal)public JedisConnectionFactory factoryTwoReal(Autowired Qualifier(redisConfigTwo) RedisStandaloneConfiguration redisConfigTwo, JedisClientConfiguration clientConfig) {return new JedisConnectionFactory(redisConfigTwo, clientConfig);}// 读取第一个redis配置Primary Bean(redisConfigOne)public RedisStandaloneConfiguration redisConfigOne() {String nodeStrAry environment.getProperty(spring.redis.redis-one.cluster.nodes);String password environment.getProperty(spring.redis.redis-one.password);assert nodeStrAry ! null;assert password ! null;RedisStandaloneConfiguration redisStandaloneConfiguration new RedisStandaloneConfiguration();String[] hostPortAry nodeStrAry.split(:);redisStandaloneConfiguration.setHostName(hostPortAry[0]);redisStandaloneConfiguration.setPort(Integer.parseInt(hostPortAry[1]));redisStandaloneConfiguration.setPassword(password);return redisStandaloneConfiguration;}// 读取第二个redis配置Bean(redisConfigTwo)public RedisStandaloneConfiguration redisConfigTwo() {String nodeStrAry environment.getProperty(spring.redis.redis-two.cluster.nodes);String password environment.getProperty(spring.redis.redis-two.password);assert nodeStrAry ! null;assert password ! null;RedisStandaloneConfiguration redisStandaloneConfiguration new RedisStandaloneConfiguration();String[] hostPortAry nodeStrAry.split(:);redisStandaloneConfiguration.setHostName(hostPortAry[0]);redisStandaloneConfiguration.setPort(Integer.parseInt(hostPortAry[1]));redisStandaloneConfiguration.setPassword(password);return redisStandaloneConfiguration;}/*集群模式 end*/Bean(clientConfig)public JedisClientConfiguration jedisClientConfiguration() {JedisPoolConfig poolConfig new JedisPoolConfig();poolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());poolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());poolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());poolConfig.setMaxWaitMillis(redisProperties.getJedis().getPool().getMaxWait().toMillis());return JedisClientConfiguration.builder().connectTimeout(redisProperties.getTimeout()).usePooling().poolConfig(poolConfig).build();}private RedisClusterConfiguration getRedisClusterConfiguration(String nodeStrAry, String password) {RedisClusterConfiguration redisClusterConfiguration new RedisClusterConfiguration();String[] serverArray nodeStrAry.split(,);SetRedisNode nodes new HashSet();for (String ipPort : serverArray) {String[] ipAndPort ipPort.split(:);nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.parseInt(ipAndPort[1])));}redisClusterConfiguration.setClusterNodes(nodes);redisClusterConfiguration.setPassword(password);return redisClusterConfiguration;}private RedisTemplateString, Object getRedisTemplate(JedisConnectionFactory factory) {RedisTemplateString, Object redisTemplate new RedisTemplate();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(factory);return redisTemplate;}/*** json序列化** return*/Beanpublic RedisSerializerObject jackson2JsonRedisSerializer() {Jackson2JsonRedisSerializerObject serializer new Jackson2JsonRedisSerializerObject(Object.class);ObjectMapper mapper new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(mapper);return serializer;}}
注解 Primary 优先考虑注入的类 Qualifier 通过控制里面的字符串来匹配类上的字符串达到控制注入的效果。
思考
factoryTwo方法与factoryTwoReal方法看起来都调用了redis-two的配置方法为什么factoryTwo实际调用的还是redis-one的配置呢
答区别就在于入参Autowired Qualifier(“redisConfigTwo”) RedisStandaloneConfiguration redisConfigTwofactoryTwo方法未使用Qualifier(“redisConfigTwo”)指定redis-two的配置方法实际上注入的是标记了Primary的redis-one配置方法。
redis工具类
为主数据源与副数据源分别创建工具类或直接使用注解指定当前使用的数据源
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** Redis主数据源工具类*/
Component
public class RedisOneUtils {// 指向redis-oneResource(name redisOneTemplate)private RedisTemplateString, Object redisTemplate;/*** 指定缓存失效时间** param key 键* param time 时间(秒)* return*/public boolean expire(String key, long time) {try {if (time 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** param key 键 不能为null* return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** param key 键* return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** param key 可以传一个值 或多个*/SuppressWarnings(unchecked)public void del(String... key) {if (key ! null key.length 0) {if (key.length 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}//String/*** 普通缓存获取** param key 键* return 值*/public Object get(String key) {return key null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** param key 键* param value 值* return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** param key 键* param value 值* param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** param key 键* param delta 要增加几(大于0)* return*/public long incr(String key, long delta) {if (delta 0) {throw new RuntimeException(递增因子必须大于0);}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** param key 键* param delta 要减少几(小于0)* return*/public long decr(String key, long delta) {if (delta 0) {throw new RuntimeException(递减因子必须大于0);}return redisTemplate.opsForValue().increment(key, -delta);}//Map/*** HashGet** param key 键 不能为null* param item 项 不能为null* return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** param key 键* return 对应的多个键值*/public MapObject, Object hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** param key 键* param map 对应多个键值* return true 成功 false 失败*/public boolean hmset(String key, MapString, Object map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** param key 键* param map 对应多个键值* param time 时间(秒)* return true成功 false失败*/public boolean hmset(String key, MapString, Object map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** param key 键* param item 项* param value 值* return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** param key 键* param item 项* param value 值* param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** param key 键 不能为null* param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** param key 键 不能为null* param item 项 不能为null* return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** param key 键* param item 项* param by 要增加几(大于0)* return*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** param key 键* param item 项* param by 要减少记(小于0)* return*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}//set/*** 根据key获取Set中的所有值** param key 键* return*/public SetObject sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** param key 键* param value 值* return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** param key 键* param values 值 可以是多个* return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** param key 键* param time 时间(秒)* param values 值 可以是多个* return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count redisTemplate.opsForSet().add(key, values);if (time 0) {expire(key, time);}return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** param key 键* return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** param key 键* param values 值 可以是多个* return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}//list/*** 获取list缓存的内容** param key 键* param start 开始* param end 结束 0 到 -1代表所有值* return*/public ListObject lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** param key 键* return*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** param key 键* param index 索引 index0时 0 表头1 第二个元素依次类推index0时-1表尾-2倒数第二个元素依次类推* return*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** param key 键* param value 值* return*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* param time 时间(秒)* return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* return*/public boolean lSet(String key, ListObject value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* param time 时间(秒)* return*/public boolean lSet(String key, ListObject value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** param key 键* param index 索引* param value 值* return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** param key 键* param count 移除多少个* param value 值* return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 模糊查询获取key值** param pattern* return*/public Set keys(String pattern) {return redisTemplate.keys(pattern);}/*** 使用Redis的消息队列** param channel* param message 消息内容*/public void convertAndSend(String channel, Object message) {redisTemplate.convertAndSend(channel, message);}/*** 根据起始结束序号遍历Redis中的list** param listKey* param start 起始序号* param end 结束序号* return*/public ListObject rangeList(String listKey, long start, long end) {//绑定操作BoundListOperationsString, Object boundValueOperations redisTemplate.boundListOps(listKey);//查询数据return boundValueOperations.range(start, end);}/*** 弹出右边的值 --- 并且移除这个值** param listKey*/public Object rifhtPop(String listKey) {//绑定操作BoundListOperationsString, Object boundValueOperations redisTemplate.boundListOps(listKey);return boundValueOperations.rightPop();}/*** 有序集合添加** param key* param value* param scoure*/public boolean zAdd(String key, Object value, double scoure) {ZSetOperationsString, Object zset redisTemplate.opsForZSet();return zset.add(key, value, scoure);}/*** 移除集合中时间过期的** param key* return*/public boolean removeSet(String key, long max) {try {long count redisTemplate.opsForZSet().removeRangeByScore(key, 0, max);System.out.println(count count);if (count 0) {return true;}} catch (Exception e) {return false;}return false;}/*** 移除集合中某个成员** param key* param value* return*/public Boolean delSetNum(String key, String value) {try {long cnt redisTemplate.opsForZSet().remove(key, value);if (cnt 0) {return true;}} catch (Exception e) {return false;}return false;}public T boolean setString(String key, T value) {try {//任意类型转换成StringString val beanToString(value);if (val null || val.length() 0) {return false;}redisTemplate.opsForValue().set(key, val);return true;} catch (Exception e) {return false;}}public T boolean setStringTime(String key, T value, long timeout) {try {//任意类型转换成StringString val beanToString(value);if (val null || val.length() 0) {return false;}redisTemplate.opsForValue().set(key, val, timeout, TimeUnit.MILLISECONDS);return true;} catch (Exception e) {return false;}}public T T get(String key, ClassT clazz) {try {Object object redisTemplate.opsForValue().get(key);if (object null) {return null;}String value String.valueOf(object);return stringToBean(value, clazz);} catch (Exception e) {return null;}}public T boolean delete(String key) {try {if (key null || key.length() 0) {return false;}redisTemplate.delete(key);return true;} catch (Exception e) {return false;}}public boolean exists(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {return false;}}SuppressWarnings(unchecked)private T T stringToBean(String value, ClassT clazz) {if (value null || value.length() 0 || clazz null) {return null;}if (clazz int.class || clazz Integer.class) {return (T) Integer.valueOf(value);} else if (clazz long.class || clazz Long.class) {return (T) Long.valueOf(value);} else if (clazz String.class) {return (T) value;} else {return JSON.toJavaObject(JSON.parseObject(value), clazz);}}/*** param value 任意类型* return String*/private T String beanToString(T value) {if (value null) {return null;}Class? clazz value.getClass();if (clazz int.class || clazz Integer.class) {return value;} else if (clazz long.class || clazz Long.class) {return value;} else if (clazz String.class) {return (String) value;} else {return JSON.toJSONString(value);}}//BoundListOperations 用法 End}