文章來(lái)源于公眾號(hào):Java極客技術(shù) 作者:鴨血粉絲
想必大家一定對(duì) Redis 不會(huì)陌生,平常工作中或多或少都會(huì)用到,不管是用來(lái)存儲(chǔ)登錄信息還是用來(lái)緩存熱點(diǎn)數(shù)據(jù),對(duì)我們來(lái)說都是很有幫助的。但是 Redis 的集群估計(jì)并不是每個(gè)人都會(huì)用到,因?yàn)楹芏鄻I(yè)務(wù)場(chǎng)景或者系統(tǒng)都是一些簡(jiǎn)單的管理系統(tǒng),并不會(huì)需要用到 Redis 的集群環(huán)境。
阿粉之前也是這樣,項(xiàng)目中用的的 Redis 是個(gè)單機(jī)環(huán)境,但是最近隨著終端量的上升,慢慢的發(fā)現(xiàn)單機(jī)已經(jīng)快支撐不住的,所以思考再三決定將 Redis 的環(huán)境升級(jí)成集群。下面阿粉給大家介紹一下在升級(jí)的過程中項(xiàng)目中需要調(diào)整的地方,這篇文章不涉及集群的搭建和配置,感興趣的同學(xué)自行搜索。
配置參數(shù)
因?yàn)檫@篇文章不介紹 Redis
集群的搭建,這里我們假設(shè)已經(jīng)有了一個(gè) Redis 的集群環(huán)境,我們項(xiàng)目中需要調(diào)整以下幾個(gè)部分
- 修改配置參數(shù),集群的節(jié)點(diǎn)和密碼配置;
- 確保引入的
Jedis
版本支持設(shè)置密碼,spring-data-redis
1.8 以上,SpringBoot
1.5 以上才支持設(shè)置密碼; - 注入
RedisTemplate
; - 編寫工具類;
修改配置參數(shù)
############### Redis 集群配置 #########################
spring.custome.redis.cluster.nodes=172.20.0.1:7001,172.20.0.2:7002,172.20.0.3:7003
spring.custome.redis.cluster.max-redirects=3
spring.custome.redis.cluster.max-active=500
spring.custome.redis.cluster.max-wait=-1
spring.custome.redis.cluster.max-idle=500
spring.custome.redis.cluster.min-idle=20
spring.custome.redis.cluster.timeout=3000
spring.custome.redis.cluster.password=redis.cluster.password
引入依賴(如果需要)
確保 SpringBoot 的版本大于 1.4.x 如果不是的話,采用如下配置,先排除 SpringBoot
中舊版本 Jedis
和 spring-data-redis
,再依賴高版本的 Jedis
和 spring-data-redis
。
org.springframework.boot
spring-boot-starter-data-redis
redis.clients
jedis
org.springframework.data
spring-data-redis
redis.clients
jedis
2.9.0
org.springframework.data
spring-data-redis
1.8.0.RELEASE
注入 RedisTemplate
注入 RedisTemplate
我們需要三個(gè)組件,分別是JedisConnectionFactory
、RedisClusterConfiguration
、JedisPoolConfig
,下面是注入RedisTempalte
的代碼。先根據(jù)配置創(chuàng)建 JedisConnectFactory
同時(shí)需要配置 RedisClusterConfiguration
、JedisPoolConfig
,最后將JedisConnectionFactory
返回用于創(chuàng)建RedisTemplate
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.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
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.StringRedisSerializer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
public class RedisClusterConfig {
@Bean(name = "redisTemplate")
@Primary
public RedisTemplate redisClusterTemplate(@Value("${spring.custome.redis.cluster.nodes}") String host,
@Value("${spring.custome.redis.cluster.password}") String password,
@Value("${spring.custome.redis.cluster.timeout}") long timeout,
@Value("${spring.custome.redis.cluster.max-redirects}") int maxRedirect,
@Value("${spring.custome.redis.cluster.max-active}") int maxActive,
@Value("${spring.custome.redis.cluster.max-wait}") int maxWait,
@Value("${spring.custome.redis.cluster.max-idle}") int maxIdle,
@Value("${spring.custome.redis.cluster.min-idle}") int minIdle) {
JedisConnectionFactory connectionFactory = jedisClusterConnectionFactory(host, password,
timeout, maxRedirect, maxActive, maxWait, maxIdle, minIdle);
return createRedisClusterTemplate(connectionFactory);
}
private JedisConnectionFactory jedisClusterConnectionFactory(String host, String password,
long timeout, int maxRedirect, int maxActive, int maxWait, int maxIdle, int minIdle) {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
List nodeList = new ArrayList();
String[] cNodes = host.split(",");
//分割出集群節(jié)點(diǎn)
for (String node : cNodes) {
String[] hp = node.split(":");
nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
}
redisClusterConfiguration.setClusterNodes(nodeList);
redisClusterConfiguration.setPassword(password);
redisClusterConfiguration.setMaxRedirects(maxRedirect);
// 連接池通用配置
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMaxTotal(maxActive);
genericObjectPoolConfig.setMinIdle(minIdle);
genericObjectPoolConfig.setMaxWaitMillis(maxWait);
genericObjectPoolConfig.setTestWhileIdle(true);
genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(300000);
JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration
.builder();
builder.connectTimeout(Duration.ofSeconds(timeout));
builder.usePooling();
builder.poolConfig(genericObjectPoolConfig);
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisClusterConfiguration, builder.build());
// 連接池初始化
connectionFactory.afterPropertiesSet();
return connectionFactory;
}
private RedisTemplate createRedisClusterTemplate(JedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(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);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
相關(guān)代碼已經(jīng)提交到GitHub,公眾號(hào)回復(fù)【源碼倉(cāng)庫(kù)】即可獲取。這里一定要注意 Jedis 的 Spring-data-redis 的版本支持設(shè)置密碼,畢竟生產(chǎn)環(huán)境是一定要配置密碼的。
編寫工具類
其實(shí)到這里基本上已經(jīng)完成了,我們可以看到 SpringBoot
項(xiàng)目接入 Redis
集群還是比較簡(jiǎn)單的,而且如果之前單機(jī)環(huán)境就是采用RedisTemplate
的話,現(xiàn)在也就不需要編寫工具類,之前的操作依舊有效。不過作為貼心的阿粉,我還是給大家準(zhǔn)備了一個(gè)工具類,代碼太長(zhǎng),我只貼部分,需要完成代碼的可以到公眾號(hào)回復(fù)【源碼倉(cāng)庫(kù)】獲取。
/**
* 刪除KEY
* @param key
* @return
*/
public boolean delete(String key) {
try {
return getTemplate().delete(key);
} catch (Exception e) {
log.error("redis hasKey() is error");
return false;
}
}
/**
* 普通緩存獲取
*
* @param key 鍵
* @return 值
*/
public Object get(String key) {
return key == null ? null : getTemplate().opsForValue().get(key);
}
/**
* 普通緩存放入
*
* @param key 鍵
* @param value 值
* @return true成功 false失敗
*/
public boolean set(String key, Object value) {
try {
getTemplate().opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.error("redis set() is error");
return false;
}
}
/**
* 普通緩存放入并設(shè)置時(shí)間
*
* @param key 鍵
* @param value 值
* @param time 時(shí)間(秒) time要大于0 如果time小于等于0 將設(shè)置無(wú)限期
* @return true成功 false 失敗
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
getTemplate().opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error("redis set() is error");
return false;
}
}
/**
* 計(jì)數(shù)器
*
* @param key 鍵
* @return 值
*/
public Long incr(String key) {
return getTemplate().opsForValue().increment(key);
}
public Long incrBy(String key, long step) {
return getTemplate().opsForValue().increment(key, step);
}
/**
* HashGet
*
* @param key 鍵 不能為null
* @param item 項(xiàng) 不能為null
* @return 值
*/
public Object hget(String key, String item) {
return getTemplate().opsForHash().get(key, item);
}
/**
* 獲取hashKey對(duì)應(yīng)的所有鍵值
*
* @param key 鍵
* @return 對(duì)應(yīng)的多個(gè)鍵值
*/
public Map hmget(String key) {
return getTemplate().opsForHash().entries(key);
}
/**
* 獲取hashKey對(duì)應(yīng)的批量鍵值
* @param key
* @param values
* @return
*/
public List<Object> hmget(String key, List values) {
return getTemplate().opsForHash().multiGet(key, values);
}
上面隨機(jī)列了幾個(gè)方法,更多方案等待你的探索。
總結(jié)
今天阿粉給大家介紹了一下 SpringBoot 項(xiàng)目如何接入 Redis 集群,需要的朋友可以參考一下,不過阿粉還是要說一下,系統(tǒng)的設(shè)計(jì)不能過于冗余,如果短期內(nèi)還能支撐業(yè)務(wù)的發(fā)展,那就暫時(shí)不要考慮太復(fù)雜,畢竟系統(tǒng)的架構(gòu)是需要不斷的完善的,不可能剛開始的時(shí)候就設(shè)計(jì)出一套很完善的系統(tǒng)框架。隨著業(yè)務(wù)的不斷發(fā)展,當(dāng)真正發(fā)現(xiàn)單機(jī) Redis 已經(jīng)無(wú)法滿足業(yè)務(wù)需求的時(shí)候再接入也不遲!
以上就是W3Cschool編程獅
關(guān)于SpringBoot 項(xiàng)目接入 Redis 集群的相關(guān)介紹了,希望對(duì)大家有所幫助。