SpringBoot 集成Redis Cluster方案

1. RedisCluster集羣結構

 

Redis-Cluster採用無中心結構,每個節點保存數據和整個集羣狀態,每個節點都和其他所有節點連接。

2. RedisCluster集羣特點

所有的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬

② 每個主節點都有一個從節點,當主節點故障,Cluster會按照規則實現主備的高可用性;

③ 可以相對平滑擴/縮容節點;

④ Redis Cluster採用虛擬槽分區,所有的key根據哈希函數映射到0~16383槽內,key是數據分區的最小粒度,因爲不能講一個大的鍵值對象,如hash,list等映射到不同的節點上;

從RedisCluster結構及其主要特點分析,當共享平臺用戶量增加時,如果業務訪問的key分佈在不通的槽分區,不同的節點上,此時即可大大增加Redis的併發訪問數,較單節點模式在性能方面會有較大的提升。

3. maven依賴整改

使用RedisCluster模式,需要使用2.9.0以上版本才能支持Redis密碼認證。由於產品共享平臺是典型的Spring項目,所以在spring-data-redis的基礎上配置spring實現操作Redis Cluster。

<--2.9.0 以下版本不支持cluster密碼認證-->
<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>2.0.8.RELEASE</version>
</dependency>

4. 項目配置文件配置項整改

RedisCluster集羣至少需要6個節點(3主3從,每個主節點分別擁有一個從節點)

#redis cluster config
#RedisCluster集羣節點及端口信息
share.redis.cluster.nodes=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
#Redis密碼
share.redis.password=
#在羣集中執行命令時要遵循的最大重定向數目
share.redis.cluster.max-redirects=5
#Redis連接池在給定時間可以分配的最大連接數。使用負值無限制
share.redis.jedis.pool.max-active=1000
#以毫秒爲單位的連接超時時間
share.redis.timeout=2000
#池中“空閒”連接的最大數量。使用負值表示無限數量的空閒連接
share.redis.jedis.pool.max-idle=8
#目標爲保持在池中的最小空閒連接數。這個設置只有在設置max-idle的情況下才有效果
share.redis.jedis.pool.min-idle=5
#連接分配在池被耗盡時拋出異常之前應該阻塞的最長時間量(以毫秒爲單位)。使用負值可以無限期地阻止
share.redis.jedis.pool.max-wait=1000
#redis cluster只使用db0
share.redis.index=0

5. 新增RedisCluster配置項類

新增RedisCluster配置項類用戶收集配置文件中的配置項

@Component
public class ShareClusterRedisProperties {
@Value("${spring.share.redis.timeout}")
    private Integer redisTimeout;
@Value("${spring.share.redis.jedis.pool.max-active}")
    private Integer poolMaxActive;
    @Value("${spring.share.redis.jedis.pool.max-idle}")
    private Integer poolMaxIdle;
    @Value("${spring.share.redis.jedis.pool.min-idle}")
    private Integer poolMinIdle;
    @Value("${spring.share.redis.jedis.pool.max-wait}")
    private Integer poolMaxWait;
    @Value("${spring.share.redis.cluster.nodes}")
    private List<String> clusterNodes;
    @Value("${spring.share.redis.cluster.max-redirects}")
    private Integer clusterMaxRedirects;
    @Value("${spring.share.redis.password}")
    private String password;
    -- getter/setter方法省略 --
}

6. 新增RedisCluster集羣配置類

@Configuration
public class ShareJedisClusterConfig {
	@Autowired
	ShareClusterRedisProperties redisProperties;
/**
     * 注意:
     * 這裏返回的JedisCluster是單例的,並且可以直接注入到其他類中去使用
     * @return
     */
    @Bean("shareJedisCluster")
    public JedisCluster getJedisCluster() {
        Set<HostAndPort> nodes = new HashSet<>();
        for (String ipPort : redisProperties.getClusterNodes()) {
            String[] ipPortPair = ipPort.split(":");
            nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
        }
        //需要密碼連接的創建對象方式
        if (StringUtils.isBlank(redisProperties.getPassword())) {
        	return new JedisCluster(nodes,redisProperties.getRedisTimeout(),1000,1,new GenericObjectPoolConfig());
        } else {
        	return new JedisCluster(nodes,redisProperties.getRedisTimeout(),1000,1,redisProperties.getPassword() ,new GenericObjectPoolConfig());
        }
    }
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(redisProperties.getPoolMaxActive());
        poolConfig.setMaxIdle(redisProperties.getPoolMaxIdle());
        poolConfig.setMinIdle(redisProperties.getPoolMinIdle());
        poolConfig.setMaxWaitMillis(redisProperties.getPoolMaxWait());
        JedisClientConfiguration clientConfig = JedisClientConfiguration.builder()
                .usePooling()
                .poolConfig(poolConfig)
                .and()
                .readTimeout(Duration.ofMillis(redisProperties.getRedisTimeout()))
                .build();
         // cluster模式
        RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
        redisConfig.setMaxRedirects(redisProperties.getClusterMaxRedirects());
        for (String ipPort :redisProperties.getClusterNodes()){
            String[] ipPortArr = ipPort.split(":");
            redisConfig.clusterNode(ipPortArr[0], Integer.parseInt(ipPortArr[1].trim()));
        }
         return new JedisConnectionFactory(redisConfig, clientConfig);
    }
}

7. Redis公共類整改

在操作Redis Cluster集羣的時候,只需自動注入JedisCluster即可,用JedisCluster替換現有的Redis公共方法中使用到redis的地方即可。

但是 JedisCluster 從性能方面等考慮,並沒有提供 Jedis 下提供的 keys 方法, keys 方法,主要用於通配符模式匹配返回滿足條件 的 key。keys 方法 還是比較有用的,所以這裏需要在Jedis的基礎上進行擴展

public int keys(final String pattern) {
    	try {
			final Set<String> keySet = new HashSet();
			final Map<String, JedisPool> nodes = jedisCluster.getClusterNodes();
			for (String k : nodes.keySet()) {
				JedisPool pool = nodes.get(k);  
			    //獲取Jedis對象,Jedis對象支持keys模糊查詢
			    Jedis jedis = pool.getResource();  
			    final Set<String> set = jedis.keys(pattern);
			    keySet.addAll(set);
			}
			
			return keySet.size();
		} catch (Exception e) {
			logger.error("異常信息", e);
            throw e;
		}
    }
    /**
     * 模糊查詢給定的pattern的所有keys列表
     *
     * @param pattern 模糊查詢
     * @return 返回當前pattern可匹配的對象keys列表
     */
    public Set<String> keyList(final String pattern) {

    	try {
			final Set<String> keySet = new HashSet<String>();
			final Map<String, JedisPool> nodes = jedisCluster.getClusterNodes();
			for (String k : nodes.keySet()) {
				JedisPool pool = nodes.get(k);  
			    //獲取Jedis對象,Jedis對象支持keys模糊查詢
			    Jedis jedis = pool.getResource();  
			    final Set<String> set = jedis.keys(pattern);
			    keySet.addAll(set);
			}
			return keySet;
		} catch (Exception e) {
			logger.error("異常信息", e);
            throw e;
		}
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章