距离上一次更新该文章已经过了 421 天,文章所描述的內容可能已经发生变化,请留意。
基于配置文件 实现 redis 动态数据源和动态数据库的切换
MultiRedisConnectionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.dao.DataAccessException;import org.springframework.data.redis.connection.*;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.util.ObjectUtils;import java.util.Map;public class MultiRedisConnectionFactory implements InitializingBean , DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory { private final Map<String, LettuceConnectionFactory> connectionFactoryMap; private static final ThreadLocal<String> CURRENT_REDIS_NAME = new ThreadLocal <>(); private static final ThreadLocal<Integer> CURRENT_REDIS_DB = new ThreadLocal <>(); public MultiRedisConnectionFactory (Map<String, LettuceConnectionFactory> connectionFactoryMap) { this .connectionFactoryMap = connectionFactoryMap; } public void setCurrentRedis (String currentRedisName) { if (!connectionFactoryMap.containsKey(currentRedisName)) { throw new RuntimeException ("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration" ); } MultiRedisConnectionFactory.CURRENT_REDIS_NAME.set(currentRedisName); } public void setCurrentRedis (String currentRedisName, Integer db) { if (!connectionFactoryMap.containsKey(currentRedisName)) { throw new RuntimeException ("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration" ); } MultiRedisConnectionFactory.CURRENT_REDIS_NAME.set(currentRedisName); MultiRedisConnectionFactory.CURRENT_REDIS_DB.set(db); } @Override public void destroy () throws Exception { connectionFactoryMap.values().forEach(LettuceConnectionFactory::destroy); MultiRedisConnectionFactory.CURRENT_REDIS_NAME.remove(); MultiRedisConnectionFactory.CURRENT_REDIS_DB.remove(); } @Override public void afterPropertiesSet () throws Exception { connectionFactoryMap.values().forEach(LettuceConnectionFactory::afterPropertiesSet); } private LettuceConnectionFactory currentLettuceConnectionFactory () { String currentRedisName = MultiRedisConnectionFactory.CURRENT_REDIS_NAME.get(); if (!ObjectUtils.isEmpty(currentRedisName)) { return connectionFactoryMap.get(currentRedisName); } return connectionFactoryMap.get(MultiRedisProperties.DEFAULT); } @Override public ReactiveRedisConnection getReactiveConnection () { return currentLettuceConnectionFactory().getReactiveConnection(); } @Override public ReactiveRedisClusterConnection getReactiveClusterConnection () { return currentLettuceConnectionFactory().getReactiveClusterConnection(); } @Override public RedisConnection getConnection () { Integer currentRedisDb = MultiRedisConnectionFactory.CURRENT_REDIS_DB.get(); if (!ObjectUtils.isEmpty(currentRedisDb)) { LettuceConnectionFactory lettuceConnectionFactory = currentLettuceConnectionFactory(); lettuceConnectionFactory.setShareNativeConnection(false ); RedisConnection connection = lettuceConnectionFactory.getConnection(); connection.select(currentRedisDb); return connection; } return currentLettuceConnectionFactory().getConnection(); } @Override public RedisClusterConnection getClusterConnection () { return currentLettuceConnectionFactory().getClusterConnection(); } @Override public boolean getConvertPipelineAndTxResults () { return currentLettuceConnectionFactory().getConvertPipelineAndTxResults(); } @Override public RedisSentinelConnection getSentinelConnection () { return currentLettuceConnectionFactory().getSentinelConnection(); } @Override public DataAccessException translateExceptionIfPossible (RuntimeException ex) { return currentLettuceConnectionFactory().translateExceptionIfPossible(ex); } }
MultiRedisProperties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;import java.util.Map;@Configuration @ConfigurationProperties(prefix = "spring.redis") public class MultiRedisProperties { public static final String DEFAULT = "release" ; private boolean enableMulti = false ; private Map<String, RedisProperties> multi; public boolean isEnableMulti () { return enableMulti; } public void setEnableMulti (boolean enableMulti) { this .enableMulti = enableMulti; } public Map<String, RedisProperties> getMulti () { return multi; } public void setMulti (Map<String, RedisProperties> multi) { this .multi = multi; } public MultiRedisProperties () { } }
RedisCustomizedConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.RedisStandaloneConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import java.util.HashMap;import java.util.Map;@ConditionalOnProperty(prefix = "spring.redis", value = "enable-multi", matchIfMissing = false) @Configuration(proxyBeanMethods = false) public class RedisCustomizedConfiguration { @Bean public MultiRedisConnectionFactory multiRedisConnectionFactory (MultiRedisProperties multiRedisProperties) { Map<String, LettuceConnectionFactory> connectionFactoryMap = new HashMap <>(); Map<String, RedisProperties> multi = multiRedisProperties.getMulti(); multi.forEach((k, v) -> { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration (); redisStandaloneConfiguration.setDatabase(v.getDatabase()); redisStandaloneConfiguration.setHostName(v.getHost()); redisStandaloneConfiguration.setPort(v.getPort()); redisStandaloneConfiguration.setUsername(v.getUsername()); redisStandaloneConfiguration.setPassword(RedisPassword.of(v.getPassword())); LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory (redisStandaloneConfiguration); connectionFactoryMap.put(k, lettuceConnectionFactory); }); return new MultiRedisConnectionFactory (connectionFactoryMap); } }
RedisConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.util.Objects;@Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate <>(); GenericJackson2JsonRedisSerializerCustomized jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializerCustomized (); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer (); template.setKeySerializer(stringRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.setConnectionFactory(redisConnectionFactory); template.afterPropertiesSet(); return template; } static class GenericJackson2JsonRedisSerializerCustomized extends GenericJackson2JsonRedisSerializer { @Override public byte [] serialize(Object source) throws SerializationException { if (Objects.nonNull(source)) { if (source instanceof String || source instanceof Character) { return source.toString().getBytes(); } } return super .serialize(source); } @Override public <T> T deserialize (byte [] source, Class<T> type) throws SerializationException { return (T) RedisSerializer.string().deserialize(source); } } }
application.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 spring: redis: enable-multi: true multi: release: host: 192.168 .56 .100 port: 6379 password: root database: 13 production: host: 192.168 .56 .100 port: 6378 password: root database: 13
测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.core.RedisTemplate;@SpringBootTest class MultiRedisSourceApplicationTests { @Autowired RedisTemplate<String, Object> redisTemplate; @Autowired MultiRedisConnectionFactory multiRedisConnectionFactory; @Test void contextLoads () { redisTemplate.opsForValue().set("k1" ,"v1" ); multiRedisConnectionFactory.setCurrentRedis("release" ,0 ); redisTemplate.opsForValue().set("k1" ,"v2" ); multiRedisConnectionFactory.setCurrentRedis("production" ,9 ); redisTemplate.opsForValue().set("k1" ,"v2" ); } }