SpringBoot 項目接入 Redis 集群
本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)」,作者鴨血粉絲 。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)公眾號。
Hello 大家好,我是鴨血粉絲,Redis 想必大家一定不會陌生,平常工作中或多或少都會用到,不管是用來存儲登錄信息還是用來緩存熱點數(shù)據(jù),對我們來說都是很有幫助的。但是 Redis 的集群估計并不是每個人都會用到,因為很多業(yè)務(wù)場景或者系統(tǒng)都是一些簡單的管理系統(tǒng),并不會需要用到 Redis 的集群環(huán)境。
阿粉之前也是這樣,項目中用的的 Redis 是個單機環(huán)境,但是最近隨著終端量的上升,慢慢的發(fā)現(xiàn)單機已經(jīng)快支撐不住的,所以思考再三決定將 Redis 的環(huán)境升級成集群。下面阿粉給大家介紹一下在升級的過程中項目中需要調(diào)整的地方,這篇文章不涉及集群的搭建和配置,感興趣的同學自行搜索。
配置參數(shù)
因為這篇文章不介紹 Redis 集群的搭建,這里我們假設(shè)已經(jīng)有了一個 Redis 的集群環(huán)境,我們項目中需要調(diào)整以下幾個部分
- 修改配置參數(shù),集群的節(jié)點和密碼配置;
- 確保引入的 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。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- <!-- 1.4 版本 SpringBoot 中 Jedis 不支持密碼登錄 -->
- <exclusions>
- <exclusion>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.springframework.data</groupId>
- <artifactId>spring-data-redis</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <!-- 手動依賴 Jedis 和 spring-data-redis-->
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- <version>2.9.0</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.data</groupId>
- <artifactId>spring-data-redis</artifactId>
- <version>1.8.0.RELEASE</version>
- </dependency>
注入 RedisTemplate
注入 RedisTemplate 我們需要三個組件,分別是JedisConnectionFactory 、RedisClusterConfiguration、JedisPoolConfig,下面是注入RedisTempalte 的代碼。先根據(jù)配置創(chuàng)建 JedisConnectFactory 同時需要配置 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<RedisNode> nodeList = new ArrayList<>();
- String[] cNodes = host.split(",");
- //分割出集群節(jié)點
- 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<String, Object> 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;
- }
- }
編寫工具類
其實到這里基本上已經(jīng)完成了,我們可以看到 SpringBoot 項目接入 Redis 集群還是比較簡單的,而且如果之前單機環(huán)境就是采用RedisTemplate 的話,現(xiàn)在也就不需要編寫工具類,之前的操作依舊有效。不過作為貼心的阿粉,我還是給大家準備了一個工具類,代碼太長,我只貼部分,需要完成代碼的可以到公眾號回復【源碼倉庫】獲取。
- /**
- * 刪除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è)置時間
- *
- * @param key 鍵
- * @param value 值
- * @param time 時間(秒) time要大于0 如果time小于等于0 將設(shè)置無限期
- * @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;
- }
- }
- /**
- * 計數(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 項 不能為null
- * @return 值
- */
- public Object hget(String key, String item) {
- return getTemplate().opsForHash().get(key, item);
- }
- /**
- * 獲取hashKey對應(yīng)的所有鍵值
- *
- * @param key 鍵
- * @return 對應(yīng)的多個鍵值
- */
- public Map<Object, Object> hmget(String key) {
- return getTemplate().opsForHash().entries(key);
- }
- /**
- * 獲取hashKey對應(yīng)的批量鍵值
- * @param key
- * @param values
- * @return
- */
- public List<Object> hmget(String key, List<String> values) {
- return getTemplate().opsForHash().multiGet(key, values);
- }
上面隨機列了幾個方法,更多方案等待你的探索。
總結(jié)
今天阿粉給大家介紹了一下 SpringBoot 項目如何接入 Redis 集群,需要的朋友可以參考一下,不過阿粉還是要說一下,系統(tǒng)的設(shè)計不能過于冗余,如果短期內(nèi)還能支撐業(yè)務(wù)的發(fā)展,那就暫時不要考慮太復雜,畢竟系統(tǒng)的架構(gòu)是需要不斷的完善的,不可能剛開始的時候就設(shè)計出一套很完善的系統(tǒng)框架。隨著業(yè)務(wù)的不斷發(fā)展,當真正發(fā)現(xiàn)單機Redis 已經(jīng)無法滿足業(yè)務(wù)需求的時候再接入也不遲!