Springboot+RedisCluster配置+重寫單機redis scan

Springboot配置RedisCLuster集羣跟單機的玩法有不少不同.node

資源文件redis

redis:
  cache:
    clusterNodes: 192.168.5.182:7111,192.168.5.182:7112,192.168.5.182:7113,192.168.5.129:7114,192.168.5.129:7115,192.168.5.129:7116
    commandTimeout: 2000
    expireSeconds: 100

兩個配置文件數組

@Component
@Data
@ConfigurationProperties(prefix = "redis.cache")
public class RedisProperties {
    private int    expireSeconds;
    private String clusterNodes;
    private int    commandTimeout;
}
@Configuration
public class JedisClusterConfig {

    @Autowired
    private RedisProperties redisProperties;

    /**
     * 注意:
     * 這裏返回的JedisCluster是單例的,而且能夠直接注入到其餘類中去使用
     * @return
     */
    @Bean
    public JedisCluster getJedisCluster() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(20);
        poolConfig.setMinIdle(10);
        poolConfig.setBlockWhenExhausted(true);
        poolConfig.setMaxWaitMillis(3000);
        poolConfig.setTestOnBorrow(false);
        poolConfig.setTestOnReturn(false);
        poolConfig.setTestWhileIdle(true);
        poolConfig.setMinEvictableIdleTimeMillis(60000);
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
        poolConfig.setNumTestsPerEvictionRun(-1);
        String[] serverArray = redisProperties.getClusterNodes().split(",");//獲取服務器數組(這裏要相信本身的輸入,因此沒有考慮空指針問題)
        Set<HostAndPort> nodes = new HashSet<>();

        for (String ipPort : serverArray) {
            String[] ipPortPair = ipPort.split(":");
            nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
        }

        return new JedisCluster(nodes, redisProperties.getCommandTimeout(),redisProperties.getExpireSeconds(),poolConfig);
    }


}

這樣咱們就能夠直接使用JedisCluster來進行集羣操做.服務器

可是JedisCluster並不支持對單機scan操做,因此咱們獲取模糊匹配的List的時候須要改寫.整體思路就是獲取Redis集羣的各個slot節點,再用scan命令以單機形式獲取各個節點的key,最後就獲取了全部節點的key.app

public class RedisUntil {
    public static List<String> getScan(Jedis redisService,String key) {
        List<String> list = new ArrayList<>();
        ScanParams params = new ScanParams();
        params.match(key);
        params.count(100);
        String cursor = "0";
        while (true) {
            ScanResult scanResult = redisService.scan(cursor,params);
            List<String> elements = scanResult.getResult();
            if (elements != null && elements.size() > 0) {
                list.addAll(elements);
            }
            cursor = scanResult.getStringCursor();
            if ("0".equals(cursor)) {
                break;
            }
        }
        return list;
    }

    public static List<String> getRedisKeys(JedisCluster jedisCluster,String matchKey) {
        List<String> list = new ArrayList<>();
        try {
            Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
            for (Map.Entry<String, JedisPool> entry : clusterNodes.entrySet()) {
                Jedis jedis = entry.getValue().getResource();
                // 判斷非從節點(由於若主從複製,從節點會跟隨主節點的變化而變化)
                if (!jedis.info("replication").contains("role:slave")) {
                    List<String> keys = getScan(jedis,matchKey);
                    if (keys.size() > 0) {
                        Map<Integer, List<String>> map = new HashMap<>();
                        for (String key : keys) {
                            // cluster模式執行多key操做的時候,這些key必須在同一個slot上,否則會報:JedisDataException:
                            // CROSSSLOT Keys in request don't hash to the same slot
                            int slot = JedisClusterCRC16.getSlot(key);
                            // 按slot將key分組,相同slot的key一塊兒提交
                            if (map.containsKey(slot)) {
                                map.get(slot).add(key);
                            } else {
                                map.put(slot, Lists.newArrayList(key));
                            }
                        }
                        for (Map.Entry<Integer, List<String>> integerListEntry : map.entrySet()) {
                           // System.out.println("integerListEntry="+integerListEntry);
                            list.addAll(integerListEntry.getValue());
                        }
                    }
                }
            }
        } finally {
            return list;
        }
    }

}

假如咱們隨便寫一個controller指針

@Autowired
private JedisCluster jedis;
@GetMapping("/users-anon/get")
public List getRedisCLuster() {
    return RedisUntil.getRedisKeys(jedis,"a*");
}

假設咱們redis集羣中有2個帶a開頭的keyserver

192.168.5.182:7112> get a1
-> Redirected to slot [7785] located at 192.168.5.182:7113
"bb"
192.168.5.182:7113> get a
-> Redirected to slot [15495] located at 192.168.5.182:7112
"b"
192.168.5.182:7112> ip

則訪問返回值爲element

["a","a1"]
相關文章
相關標籤/搜索