Redis集羣是無法執行批量操做命令的,如mget,pipeline等。這是由於redis將集羣劃分爲16383個哈希槽,不一樣的key會劃分到不一樣的槽中。可是,Jedis客戶端提供了計算key的slot方法,已經slot和節點之間的映射關係,經過這兩個數據,就能夠計算出每一個key所在的節點,而後使用pipeline獲取數據。具體代碼以下:node
初始化 JedisCluster類redis
@Configuration public class JedisClusterConfig { @Value("${spring.redis.cluster.nodes}") private String clusterNodes; @Value("${spring.redis.cache.commandTimeout}") private Integer commandTimeout; @Bean public JedisCluster getJedisCluster() { String[] serverArray = clusterNodes.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, commandTimeout); } }
工具類 JedisClusterUtilspring
@Component public class JedisClusterUtil { @Autowired private JedisCluster jedisCluster; @Resource(name = "redisTemplate4Json") protected RedisTemplate<String, Object> redisTemplate; /** * ZSet批量查詢 * @param keys * @return */ public List<Object> batchZRange(List<String> keys) { List<Object> resList = new ArrayList<>(); if (keys == null || keys.size() == 0) { return resList; } if (keys.size() == 1) { BoundZSetOperations<String, Object> operations = redisTemplate.boundZSetOps(keys.get(0)); Set<Object> set = operations.reverseRange(0, 0); resList.add(set.iterator().next()); return resList; } Map<JedisPool, List<String>> jedisPoolMap = getJedisPool(keys); List<String> keyList; JedisPool currentJedisPool = null; Pipeline currentPipeline; List<Object> res = new ArrayList<>(); Map<String, Object> resultMap = new HashMap<>(); //執行 for (Map.Entry<JedisPool, List<String>> entry : jedisPoolMap.entrySet()) { Jedis jedis = null; try { currentJedisPool = entry.getKey(); keyList = entry.getValue(); //獲取pipeline jedis = currentJedisPool.getResource(); currentPipeline = jedis.pipelined(); for (String key : keyList) { currentPipeline.zrevrange(key, 0, 0); } //從pipeline中獲取結果 res = currentPipeline.syncAndReturnAll(); currentPipeline.close(); for (int i = 0; i < keyList.size(); i++) { if (null == res.get(i)) { resultMap.put(keyList.get(i), null); } else { Set<Object> set = (Set<Object>) res.get(i); if (null == set || set.isEmpty()) { resultMap.put(keyList.get(i), null); } else { byte[] byteStr = set.iterator().next().toString().getBytes(); Object obj = redisTemplate.getDefaultSerializer().deserialize(byteStr); resultMap.put(keyList.get(i), obj); } } } } catch (Exception e) { e.printStackTrace(); } finally { returnResource(jedis, currentJedisPool); } } resList = sortList(keys, resultMap); return resList; } /** * Value批量查詢 * @param keys * @return */ public List<Object> batchGet(List<String> keys){ List<Object> resList = new ArrayList<>(); if (keys == null || keys.size() == 0) { return resList; } if (keys.size() == 1) { BoundValueOperations<String, Object> operations = redisTemplate.boundValueOps(keys.get(0)); resList.add(operations.get()); return resList; } Map<JedisPool, List<String>> jedisPoolMap = getJedisPool(keys); List<String> keyList; JedisPool currentJedisPool = null; Pipeline currentPipeline; List<Object> res = new ArrayList<>(); Map<String, Object> resultMap = new HashMap<>(); for (Map.Entry<JedisPool, List<String>> entry : jedisPoolMap.entrySet()) { Jedis jedis = null; try { currentJedisPool = entry.getKey(); keyList = entry.getValue(); //獲取pipeline jedis = currentJedisPool.getResource(); currentPipeline = jedis.pipelined(); for (String key : keyList) { currentPipeline.get(key); } //從pipeline中獲取結果 res = currentPipeline.syncAndReturnAll(); currentPipeline.close(); for (int i = 0; i < keyList.size(); i++) { if (null == res.get(i)) { resultMap.put(keyList.get(i), null); } else { byte[] byteStr = keyList.get(i).toString().getBytes(); Object obj = redisTemplate.getDefaultSerializer().deserialize(byteStr); resultMap.put(keyList.get(i), obj); } } } catch (Exception e) { e.printStackTrace(); } finally { returnResource(jedis, currentJedisPool); } } resList = sortList(keys, resultMap); return resList; } private Map<JedisPool, List<String>> getJedisPool(List<String> keys){ //JedisCluster繼承了BinaryJedisCluster //BinaryJedisCluster的JedisClusterConnectionHandler屬性 //裏面有JedisClusterInfoCache,根據這一條繼承鏈,能夠獲取到JedisClusterInfoCache //從而獲取slot和JedisPool直接的映射 MetaObject metaObject = SystemMetaObject.forObject(jedisCluster); JedisClusterInfoCache cache = (JedisClusterInfoCache) metaObject.getValue("connectionHandler.cache"); //保存地址+端口和命令的映射 Map<JedisPool, List<String>> jedisPoolMap = new HashMap<>(); JedisPool currentJedisPool = null; List<String> keyList; for (String key : keys) { //計算哈希槽 int crc = JedisClusterCRC16.getSlot(key); //經過哈希槽獲取節點的鏈接 currentJedisPool = cache.getSlotPool(crc); //因爲JedisPool做爲value保存在JedisClusterInfoCache中的一個map對象中,每一個節點的 //JedisPool在map的初始化階段就是肯定的和惟一的,因此獲取到的每一個節點的JedisPool都是同樣 //的,能夠做爲map的key if (jedisPoolMap.containsKey(currentJedisPool)) { jedisPoolMap.get(currentJedisPool).add(key); } else { keyList = new ArrayList<>(); keyList.add(key); jedisPoolMap.put(currentJedisPool, keyList); } } return jedisPoolMap; } private List<Object> sortList(List<String> keys, Map<String, Object> params) { List<Object> resultList = new ArrayList<>(); Iterator<String> it = keys.iterator(); while (it.hasNext()) { String key = it.next(); resultList.add(params.get(key)); } return resultList; } /** * 釋放jedis資源 * * @param jedis */ public void returnResource(Jedis jedis, JedisPool jedisPool) { if (jedis != null && jedisPool != null) { jedisPool.returnResource(jedis); } }
注意:必定要完成後釋放 jedis 資源 否則會形成卡死現象工具