詳解SpringBoot Redis自適應(yīng)配置(Cluster Standalone Sentinel)
核心代碼段
提供一個(gè)JedisConnectionFactory 根據(jù)配置來判斷 單點(diǎn) 集群 還是哨兵
@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)配置錯(cuò)誤!請(qǐng)確認(rèn)node=[%s]是否正確", node));
}
}
//獲得默認(rèn)的連接池構(gòu)造器(怎么設(shè)計(jì)的,為什么不抽象出單獨(dú)類,供用戶使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig來修改默認(rèn)的連接池構(gòu)造器(真麻煩,濫用設(shè)計(jì)模式?。??。。?!
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)配置錯(cuò)誤!請(qǐng)確認(rèn)node=[%s]是否正確", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是單個(gè)節(jié)點(diǎn) 用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);
}
}
//集群配置信息實(shí)現(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配置
* 適用于 單點(diǎn)[主從] 哨兵模式 集群模式
* @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() {
// 初始化緩存管理器,在這里我們可以緩存的整體過期時(shí)間什么的,我這里默認(rèn)沒有配置
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ā)生異常時(shí),打印日志,但是程序正常走
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ù)(如果設(shè)置為阻塞時(shí)BlockWhenExhausted),如果超時(shí)就拋異常, 小于零:阻塞不確定的時(shí)間, 默認(rèn)-1
config.setMaxWaitMillis(maxWaitMillis);
//最小空閑連接數(shù), 默認(rèn)0
config.setMinIdle(minIdle);
// 最大空閑連接數(shù), 默認(rèn)8個(gè)
config.setMaxIdle(maxIdle);
// 最大連接數(shù), 默認(rèn)值8個(gè)
config.setMaxTotal(maxTotal);
//對(duì)拿到的connection進(jìn)行validateObject校驗(yàn)
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)配置錯(cuò)誤!請(qǐng)確認(rèn)node=[%s]是否正確", node));
}
}
//獲得默認(rèn)的連接池構(gòu)造器(怎么設(shè)計(jì)的,為什么不抽象出單獨(dú)類,供用戶使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig來修改默認(rèn)的連接池構(gòu)造器(真麻煩,濫用設(shè)計(jì)模式?。??。。?!
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)配置錯(cuò)誤!請(qǐng)確認(rèn)node=[%s]是否正確", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是單個(gè)節(jié)點(diǎn) 用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);
}
}
//集群配置信息實(shí)現(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有多個(gè)redis的地址,以逗號(hào)分隔
- 如果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模式 單點(diǎn)
node里只有一個(gè)redis地址的時(shí)候,會(huì)自動(dòng)變成單點(diǎn)模式 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以后就變成了哨兵模式 多個(gè)哨兵可以以逗號(hào)分割 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覆蓋默認(rèn)配置
如果沒有配置這些信息,就會(huì)走默認(rèn)配置 也可以在properties或者yml覆蓋默認(rèn)配置
#最大連接數(shù), 默認(rèn)值8個(gè) spring.redis.jedis.pool.max-total=8 #最大空閑連接數(shù), 默認(rèn)8個(gè) spring.redis.jedis.pool.max-idle=8 #最小空閑連接數(shù), 默認(rèn)0 spring.redis.jedis.pool.min-idle=0 #獲取連接時(shí)的最大等待毫秒數(shù),如果超時(shí)就拋異常, 小于零:阻塞不確定的時(shí)間, 默認(rèn)-1 spring.redis.jedis.pool.max-wait=-1 #對(duì)拿到的connection進(jìn)行validateObject校驗(yàn) spring.redis.jedis.pool.test-on-borrow=true #集群時(shí)最大重定向個(gè)數(shù)默認(rèn)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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Sentinel原理與SpringBoot整合實(shí)戰(zhàn)案例講解
- Springboot?中使用Sentinel的詳細(xì)步驟
- springboot整合sentinel接口熔斷的實(shí)現(xiàn)示例
- 在SpringBoot項(xiàng)目中使用Spring Cloud Sentinel實(shí)現(xiàn)流量控制
- springboot?整合sentinel的示例代碼
- 詳解Springboot集成sentinel實(shí)現(xiàn)接口限流入門
- SpringBoot2.0+阿里巴巴Sentinel動(dòng)態(tài)限流實(shí)戰(zhàn)(附源碼)
- springboot整合sentinel的方法教程
- SpringBoot基于Sentinel在服務(wù)上實(shí)現(xiàn)接口限流
- SpringBoot整合Sentinel啟動(dòng)失敗及運(yùn)行時(shí)常見錯(cuò)誤總結(jié)
相關(guān)文章
SpringBoot+?Sharding?Sphere?輕松實(shí)現(xiàn)數(shù)據(jù)庫字段加解密功能
在Spring?Boot生態(tài)中,有一個(gè)非常厲害的開源框架:Apache?ShardingSphere,它是一款分布式?SQL?事務(wù)和查詢引擎,可通過數(shù)據(jù)分片、彈性伸縮、加密等能力對(duì)任意數(shù)據(jù)庫進(jìn)行增強(qiáng),今天通過這篇文章,我們一起來了解一下如何在?Spring?Boot?中快速實(shí)現(xiàn)數(shù)據(jù)的加解密功能2024-07-07
Java遞歸實(shí)現(xiàn)斐波那契數(shù)列
這篇文章主要為大家詳細(xì)介紹了Java遞歸實(shí)現(xiàn)斐波那契數(shù)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Java利用轉(zhuǎn)義字符過濾HTML中的標(biāo)簽
通過本文介紹的Java方法,我們可以輕松利用轉(zhuǎn)義字符來過濾HTML文本中的標(biāo)簽,確保輸出內(nèi)容的純文本展示,感興趣的朋友跟隨小編一起看看吧2024-11-11
Java操作PDF文件實(shí)現(xiàn)簽訂電子合同詳細(xì)教程
這篇文章主要介紹了如何在PDF中加入電子簽章與電子簽名的過程,包括編寫Word文件、生成PDF、為PDF格式做表單、為表單賦值、生成文檔以及上傳到OBS中的步驟,需要的朋友可以參考下2025-01-01
java 數(shù)據(jù)結(jié)構(gòu)單鏈表的實(shí)現(xiàn)
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)單鏈表的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2017-07-07
SpringBoot內(nèi)置數(shù)據(jù)源的持久化與解決方案
數(shù)據(jù)源的配置 我們先基于SpringBoot默認(rèn)的HikariDataSource數(shù)據(jù)源,導(dǎo)入JDBC場景,看看SpringBoot幫我們自動(dòng)配置了什么,下面我們來了解SpringBoot內(nèi)置數(shù)據(jù)源持久化2022-07-07

