Springboot配置RedisCLuster集群跟单机的玩法有很多不一样.
资源文件
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.
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开头的key
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>
则访问返回值为
["a","a1"]