詳解SpringBoot Redis自適應(yīng)配置(Cluster Standalone Sentinel)
核心代碼段
提供一個JedisConnectionFactory 根據(jù)配置來判斷 單點 集群 還是哨兵
@Bean
@ConditionalOnMissingBean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = null;
String[] split = node.split(",");
Set<HostAndPort> nodes = new LinkedHashSet<>();
for (int i = 0; i < split.length; i++) {
try {
String[] split1 = split[i].split(":");
nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出現(xiàn)配置錯誤!請確認node=[%s]是否正確", node));
}
}
//獲得默認的連接池構(gòu)造器(怎么設(shè)計的,為什么不抽象出單獨類,供用戶使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig來修改默認的連接池構(gòu)造器(真麻煩,濫用設(shè)計模式?。??。。?!
jpcb.poolConfig(jedisPoolConfig);
//通過構(gòu)造器來構(gòu)造jedis客戶端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//如果是哨兵的模式
if (!StringUtils.isEmpty(sentinel)) {
logger.info("Redis use SentinelConfiguration");
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
String[] sentinelArray = sentinel.split(",");
for (String s : sentinelArray) {
try {
String[] split1 = s.split(":");
redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出現(xiàn)配置錯誤!請確認node=[%s]是否正確", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是單個節(jié)點 用Standalone模式
else if (nodes.size() == 1) {
logger.info("Redis use RedisStandaloneConfiguration");
for (HostAndPort n : nodes) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
if (!StringUtils.isEmpty(password)) {
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
}
redisStandaloneConfiguration.setPort(n.getPort());
redisStandaloneConfiguration.setHostName(n.getHost());
factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
}
//集群配置信息實現(xiàn)
else {
logger.info("Redis use RedisStandaloneConfiguration");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
nodes.forEach(n -> {
redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
});
if (!StringUtils.isEmpty(password)) {
redisClusterConfiguration.setPassword(RedisPassword.of(password));
}
redisClusterConfiguration.setMaxRedirects(maxRedirect);
factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
return factory;
}
Configration
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.*;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @Author foxzzz
* @Class SelfAdaptionRedisConfig
* @Description 自適應(yīng)redis配置
* 適用于 單點[主從] 哨兵模式 集群模式
* @Date 2020/7/6 14:34
*/
@Configuration
@EnableCaching
public class SelfAdaptionRedisConfig<K, V> extends CachingConfigurerSupport {
private final static Logger logger = LoggerFactory.getLogger(SelfAdaptionRedisConfig.class);
@Value("${spring.redis.node}")
private String node;
@Value("${spring.redis.timeout:0}")
private int timeout;
@Value("${spring.redis.password:}")
private String password;
@Value("${spring.redis.sentinel:}")
private String sentinel;
@Value("${spring.redis.jedis.pool.max-total:8}")
private int maxTotal;
@Value("${spring.redis.jedis.pool.max-idle:8}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle:0}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait:-1}")
private long maxWaitMillis;
@Value("${spring.redis.jedis.pool.test-on-borrow:true}")
private boolean testOnBorrow;
@Value("${spring.redis.jedis.factory.max-redirects:5}")
private int maxRedirect;
@Autowired
private JedisPoolConfig jedisPoolConfig;
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
@Bean
@ConditionalOnMissingBean
@Override
public CacheManager cacheManager() {
// 初始化緩存管理器,在這里我們可以緩存的整體過期時間什么的,我這里默認沒有配置
logger.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(jedisConnectionFactory);
return builder.build();
}
@Bean
@ConditionalOnMissingBean
@Override
public CacheErrorHandler errorHandler() {
// 異常處理,當(dāng)Redis發(fā)生異常時,打印日志,但是程序正常走
logger.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
logger.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
logger.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
logger.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
logger.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
@Bean
@ConditionalOnMissingBean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
// 獲取連接時的最大等待毫秒數(shù)(如果設(shè)置為阻塞時BlockWhenExhausted),如果超時就拋異常, 小于零:阻塞不確定的時間, 默認-1
config.setMaxWaitMillis(maxWaitMillis);
//最小空閑連接數(shù), 默認0
config.setMinIdle(minIdle);
// 最大空閑連接數(shù), 默認8個
config.setMaxIdle(maxIdle);
// 最大連接數(shù), 默認值8個
config.setMaxTotal(maxTotal);
//對拿到的connection進行validateObject校驗
config.setTestOnBorrow(testOnBorrow);
return config;
}
// private JedisCluster getJedisCluster() {
// String[] split = node.split(",");
// Set<HostAndPort> nodes = new LinkedHashSet<>();
// for (int i = 0; i < split.length; i++) {
// try {
// String[] split1 = split[i].split(":");
// nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1]))));
// } catch (Exception e) {
// }
// }
// JedisCluster jedisCluster = null;
// if (StringUtils.isEmpty(password)) {
// jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, jedisPoolConfig);
// } else {
// jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, password, jedisPoolConfig);
// }
// }
@Bean
@ConditionalOnMissingBean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = null;
String[] split = node.split(",");
Set<HostAndPort> nodes = new LinkedHashSet<>();
for (int i = 0; i < split.length; i++) {
try {
String[] split1 = split[i].split(":");
nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出現(xiàn)配置錯誤!請確認node=[%s]是否正確", node));
}
}
//獲得默認的連接池構(gòu)造器(怎么設(shè)計的,為什么不抽象出單獨類,供用戶使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig來修改默認的連接池構(gòu)造器(真麻煩,濫用設(shè)計模式?。??。。。?
jpcb.poolConfig(jedisPoolConfig);
//通過構(gòu)造器來構(gòu)造jedis客戶端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//如果是哨兵的模式
if (!StringUtils.isEmpty(sentinel)) {
logger.info("Redis use SentinelConfiguration");
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
String[] sentinelArray = sentinel.split(",");
for (String s : sentinelArray) {
try {
String[] split1 = s.split(":");
redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出現(xiàn)配置錯誤!請確認node=[%s]是否正確", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是單個節(jié)點 用Standalone模式
else if (nodes.size() == 1) {
logger.info("Redis use RedisStandaloneConfiguration");
for (HostAndPort n : nodes) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
if (!StringUtils.isEmpty(password)) {
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
}
redisStandaloneConfiguration.setPort(n.getPort());
redisStandaloneConfiguration.setHostName(n.getHost());
factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
}
//集群配置信息實現(xiàn)
else {
logger.info("Redis use RedisStandaloneConfiguration");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
nodes.forEach(n -> {
redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
});
if (!StringUtils.isEmpty(password)) {
redisClusterConfiguration.setPassword(RedisPassword.of(password));
}
redisClusterConfiguration.setMaxRedirects(maxRedirect);
factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
return factory;
}
@Bean
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
//設(shè)置序列化
Jackson2JsonRedisSerializer 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
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
RedisSerializer stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash key序列化
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
pom依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${springboot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
配置文件
1.yml配置
1.1支持 Cluster模式 集群
- node有多個redis的地址,以逗號分隔
- 如果redis沒有密碼直接去掉配置就可以了 yml配置
spring: redis: node: 127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003 password: 123456
properties配置
spring.redis.node=127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003 spring.redis.password=123456
1.2支持 Standalone模式 單點
node里只有一個redis地址的時候,會自動變成單點模式 yml配置
spring: redis: node: 127.0.0.1:1001 password: 123456
properties配置
spring.redis.node=127.0.0.1:1001 spring.redis.password=123456
1.3支持 Sentinel模式 哨兵
當(dāng)配置上sentinel以后就變成了哨兵模式 多個哨兵可以以逗號分割 yml配置
spring: redis: node: 127.0.0.1:1001 sentinel:127.0.0.1:1002,127.0.0.1:1003 password: 123456
properties配置
spring.redis.node=127.0.0.1:1001 spring.redis.sentinel:127.0.0.1:1002,127.0.0.1:1003 spring.redis.password=123456
1.4覆蓋默認配置
如果沒有配置這些信息,就會走默認配置 也可以在properties或者yml覆蓋默認配置
#最大連接數(shù), 默認值8個 spring.redis.jedis.pool.max-total=8 #最大空閑連接數(shù), 默認8個 spring.redis.jedis.pool.max-idle=8 #最小空閑連接數(shù), 默認0 spring.redis.jedis.pool.min-idle=0 #獲取連接時的最大等待毫秒數(shù),如果超時就拋異常, 小于零:阻塞不確定的時間, 默認-1 spring.redis.jedis.pool.max-wait=-1 #對拿到的connection進行validateObject校驗 spring.redis.jedis.pool.test-on-borrow=true #集群時最大重定向個數(shù)默認5 spring.redis.jedis.factory.max-redirects=5
使用
代碼使用
@Autowired private RedisTemplate<String, Object> redisTemplate;
到此這篇關(guān)于詳解SpringBoot Redis自適應(yīng)配置(Cluster Standalone Sentinel)的文章就介紹到這了,更多相關(guān)SpringBoot Redis自適應(yīng)配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結(jié)
這篇文章主要介紹了IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結(jié),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
詳解springboot接口如何優(yōu)雅的接收時間類型參數(shù)
這篇文章主要為大家詳細介紹了springboot的接口如何優(yōu)雅的接收時間類型參數(shù),文中為大家整理了三種常見的方法,希望對大家有一定的幫助2023-09-09
簡單了解Java多態(tài)向上轉(zhuǎn)型相關(guān)原理
這篇文章主要介紹了簡單了解Java多態(tài)向上轉(zhuǎn)型相關(guān)原理,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12
knife4j+springboot3.4異常無法正確展示文檔
本文主要介紹了knife4j+springboot3.4異常無法正確展示文檔,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
基于Lucene的Java搜索服務(wù)器Elasticsearch安裝使用教程
Elasticsearch也是用Java開發(fā)的,并作為Apache許可條款下的開放源碼發(fā)布,能夠做到實時搜索,且穩(wěn)定、可靠、快速,安裝使用方便,這里我們就來看一下基于Lucene的Java搜索服務(wù)器Elasticsearch安裝使用教程:2016-06-06
Java SimpleDateFormat線程安全問題原理詳解
這篇文章主要介紹了Java SimpleDateFormat線程安全問題原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-05-05

