一、实现原理
一致性哈希算法(Consistent Hashing): http://www.zsythink.net/archives/1182
二、配置两个redis服务,端口号要不一致
三、代码
1. 配置文件 redis1.ip = 127.0.0.1 redis1.port=6379 redis2.ip = 127.0.0.1 redis2.port=6380 #最大连接数 redis.max.total=20 #最大空闲数 redis.max.idle=10 #最小空闲数 redis.min.idle=2 #效验使用可用连接 redis.test.borrow=true #效验归还可用连接 redis.test.return=false
1 package com.mmall.common; 2 3 import com.mmall.util.PropertiesUtil; 4 import redis.clients.jedis.JedisPoolConfig; 5 import redis.clients.jedis.JedisShardInfo; 6 import redis.clients.jedis.ShardedJedis; 7 import redis.clients.jedis.ShardedJedisPool; 8 import redis.clients.util.Hashing; 9 import redis.clients.util.Sharded; 10 import java.util.ArrayList; 11 import java.util.List; 12 13 /** 14 * redis分片连接池 15 */ 16 public class RedisShardedPool { 17 private static ShardedJedisPool pool ; //jedis连接池 18 private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total","20")); //最大连接数 19 private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle","10")); //最大空闲状态 20 private static Integer minIdle =Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle","2")); //最小空闲状态 21 22 private static Boolean testOnBorrow =Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow","true")); //验证从连接池拿出的jedis实例,一定可用 23 private static Boolean testOnReturn =Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return","true")); //验证还回连接池的jedis实例,一定可用 24 25 private static String redis1Ip =PropertiesUtil.getProperty("redis1.ip"); //最小空闲状态 26 private static Integer redis1Port =Integer.parseInt(PropertiesUtil.getProperty("redis1.port")); //最小空闲状态 27 private static String redis2Ip =PropertiesUtil.getProperty("redis2.ip"); //最小空闲状态 28 private static Integer redis2Port =Integer.parseInt(PropertiesUtil.getProperty("redis2.port")); //最小空闲状态 29 30 private static void initPool(){ 31 JedisPoolConfig config = new JedisPoolConfig(); 32 config.setMaxTotal(maxTotal); 33 config.setMaxIdle(maxIdle); 34 config.setMinIdle(minIdle); 35 36 config.setTestOnBorrow(testOnBorrow); 37 config.setTestOnReturn(testOnReturn); 38 39 config.setBlockWhenExhausted(true); //连接耗尽时是否阻塞,false抛出异常;true阻塞到超时。默认true 40 41 JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2); 42 JedisShardInfo info2 = new JedisShardInfo(redis2Ip,redis2Port,1000*2); 43 List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>(2); 44 jedisShardInfoList.add(info1); 45 jedisShardInfoList.add(info2); 46 47 pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN); 48 } 49 50 static{ 51 initPool(); 52 } 53 54 public static ShardedJedis getJedis(){ 55 return pool.getResource(); 56 } 57 58 /** 59 * redis不正常不可用,将其废弃,最新版本直接将此连接销毁jedis.close(); 60 * @param jedis 61 */ 62 public static void returnBrokenResource(ShardedJedis jedis){ 63 pool.returnBrokenResource(jedis); 64 } 65 66 public static void returnResource(ShardedJedis jedis){ 67 pool.returnResource(jedis); 68 } 69 70 public static void main(String[] args) { 71 ShardedJedis shardedJedis = pool.getResource(); 72 for (int i = 0; i<10; i++){ 73 shardedJedis.set("key"+i,"value"+i); 74 } 75 returnResource(shardedJedis); //使用后的连接放回连接池中 76 // pool.destroy(); // 销毁连接池的所有连接 77 System.out.println("program is end"); 78 79 } 80 }
运行main方法,查询redis生成的数据:
封装常用redisAPI
import com.mmall.common.RedisShardedPool; import lombok.extern.slf4j.Slf4j; import redis.clients.jedis.Jedis; import redis.clients.jedis.ShardedJedis; /** * 封装分片redis */ @Slf4j public class RedisShardedPoolUtil { /** * 设置对应key的有效期 * @param key * @param exTime 有效期,单位秒 * @return */ public static Long expire(String key, int exTime){ ShardedJedis shardedJedis = null; Long result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.expire(key,exTime); }catch (Exception e){ log.error("set key:{} exTime:{} value:{} error",key,exTime,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } /** * string 添加,存在有效期exTime * @param key 键 * @param value 值 * @param exTime 有效期,单位秒 * @return */ public static String setEx(String key, String value, int exTime){ ShardedJedis shardedJedis = null; String result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.setex(key,exTime,value); }catch (Exception e){ log.error("set key:{} exTime:{} value:{} error",key,exTime,value,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } /** * string 添加 * @param key * @param value * @return */ public static String set(String key, String value){ ShardedJedis shardedJedis = null; String result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.set(key,value); }catch (Exception e){ log.error("set key:{} value:{} error",key,value,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } /** * string 获取 * @param key * @return */ public static String get(String key){ ShardedJedis shardedJedis = null; String result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.get(key); }catch (Exception e){ log.error("get key:{} error",key,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } public static String getSet(String key, String value){ ShardedJedis shardedJedis = null; String result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.getSet(key,value); }catch (Exception e){ log.error("set key:{} value:{} error",key,value,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } /** * stirng 删除 * @param key * @return */ public static Long del(String key){ ShardedJedis shardedJedis = null; Long result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.del(key); }catch (Exception e){ log.error("get key:{} error",key,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } public static Long setnx(String key, String value){ ShardedJedis shardedJedis = null; Long result = null; try{ shardedJedis = RedisShardedPool.getJedis(); result = shardedJedis.setnx(key,value); }catch (Exception e){ log.error("set key:{} value:{} error",key,value,e); RedisShardedPool.returnBrokenResource(shardedJedis); return result; } RedisShardedPool.returnResource(shardedJedis); return result; } }
四、ShardedJedis相关类介绍
(1)、ShardedJedisPool
ShardedJedis是基于一致性哈希算法实现的分布式Redis集群客户端;ShardedJedis的设计分为以下几块:
对象池设计:Pool,ShardedJedisPool,ShardedJedisFactory
面向用户的操作封装:BinaryShardedJedis,BinaryShardedJedis
一致性哈希实现:Sharded
Sharded一致性哈希实现
shared一致性哈希采用以下方案:
Redis服务器节点划分:将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重)
将划分虚拟节点采用TreeMap存储
对每个Redis服务器的物理连接采用LinkedHashMap存储
对Key or KeyTag 采用同样的hash算法,然后从TreeMap获取大于等于键hash值得节点,取最邻近节点存储;当key的hash值大于虚拟节点hash值得最大值时,存入第一个虚拟节点。
(2)、JedisShardInfo
配置每台redis服务器的连接
JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2);