(七)使用jedis连接单机和集群(一步一个坑踩出来的辛酸泪)
环境准备:
-
redis-4.0.9,最新版了
-
ruby:redis-x.x.x.gem 这个gem什么版本都行,我redis4用3.0.0的gem正常跑
-
jedis-2.9.0.jar,最新版
伪集群搭建:这里省略了,需要的看我前面的文档,这里只贴出来一些关键点
1、下载、解压
1 # make 2 # make install PREFIX=/usr/local/bin/
2、创建集群工作目录,把redis/bin复制进去,需要几台集群自己看着办
3、修改每个节点的配置文件
1 port 7001 //端口7001,7002,7003... 2 bind 本机ip //默认ip为127.0.0.1 需要改为其他节点机器可访问的ip 否则创建集群时无法访问对应的端口,无法创建集群,直接注释掉 3 daemonize yes //redis后台运行 4 cluster-enabled yes //开启集群 把注释#去掉
protect_mode no //如果bind注释掉了,那么就把保护模式关掉
5 #可选项 6 cluster-config-file nodes.conf //集群的配置 配置文件首次启动自动生成,默认就行
cluster-node-timeout 15000 //请求超时 默认15秒,可自行设置
appendonly yes //aof日志开启 有需要就开启,它会每次写操作都记录一条日志
4、安装ruby、rubygems、redis.x.x.x.gem
1 # yum install ruby 2 # yum install rubygems 3 # 去官网下redis-x.x.x.gem,3.0以上都行 4 # gem install redis-4.0.1.gem
5、构建以下目录
解释:
redis-cli:客户端,方便调试用
reids-trib.rb:集群的ruby脚本,去源码包复制过来
start-all-nodes.h:shell脚本,启动所有节点(参考下图)
stop-all-nodes.h:shell脚本,关闭集群(参考下图)
redis-cluster-configure.sh:开始集群部署,需要node节点全部启动(参考下图)
6、依次执行shell即可(start-all-nodes.sh ---> redis-cluster-configure.sh ---> input "yes")
集群测试:
1 @Test 2 public void testCluster() throws IOException, InterruptedException { 3 Set<HostAndPort> nodes = new HashSet<>(); 4 nodes.add(new HostAndPort("47.100.101.31", 7001)); 5 nodes.add(new HostAndPort("47.100.101.31", 7002)); 6 nodes.add(new HostAndPort("47.100.101.31", 7003)); 7 nodes.add(new HostAndPort("47.100.101.31", 7004)); 8 nodes.add(new HostAndPort("47.100.101.31", 7005)); 9 nodes.add(new HostAndPort("47.100.101.31", 7006)); 10 JedisCluster cluster = new JedisCluster(nodes); 11 try { 12 String res = cluster.get("name"); 13 System.out.println(res); 14 cluster.close(); 15 } catch (Exception e) { 16 e.printStackTrace(); 17 cluster.close(); 18 } 19 }
遇到的坑:
1)连接报错:Connection refused。
错误原因:远程拒绝链接,单机版和集群都报错。
解决方案:当时bind 127.0.0.1。于是乎注释掉,不行,提示没有bind于是启动了保护模式,所以配置:protect_mode no,单机版ok,redis客户端连集群ok。
2)通过jedis连接redis单机成功,使用JedisCluster连接redis集群一直报Could not get a resource from the pool,但是使用redis客户端可以连接集群(我使用的redis desktop manager)在java中通过jedis连接redis单机也成功,但使用JedisCluster连接redis集群一直报Could not get a resource from the pool,
错误原因:未知,以为jar包版本问题,换最新版2.9.0.jar,无效。纠结了好几天,最后看一个博客说把127.0.0.1换成公网ip
解决:127.0.0.1换成公网IP,于是直接跑通。
疑问:127.0.0.1表示的就是本地,启动后对外网来说127就是公网ip了,不知道为啥会错,因为用其他客户端都没问题,怀疑是jedis内部机制导致的。
附录:Spring集成redis
1 <!-- Jedis连接池配置, 可以写在资源文件中 --> 2 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> 3 <!-- 最大连接数 --> 4 <property name="maxTotal" value="30" /> 5 <!-- 最大空闲连接数 --> 6 <property name="maxIdle" value="10" /> 7 <!-- 每次释放连接的最大数目 --> 8 <property name="numTestsPerEvictionRun" value="1024" /> 9 <!-- 释放连接的扫描间隔(毫秒) --> 10 <property name="timeBetweenEvictionRunsMillis" value="30000" /> 11 <!-- 连接最小空闲时间 --> 12 <property name="minEvictableIdleTimeMillis" value="1800000" /> 13 <!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 --> 14 <property name="softMinEvictableIdleTimeMillis" value="10000" /> 15 <!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 --> 16 <property name="maxWaitMillis" value="1500" /> 17 <!-- 在获取连接的时候检查有效性, 默认false --> 18 <property name="testOnBorrow" value="true" /> 19 <!-- 在空闲时检查有效性, 默认false --> 20 <property name="testWhileIdle" value="true" /> 21 <!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true --> 22 <property name="blockWhenExhausted" value="false" /> 23 </bean> 24 <!-- jedis客户端单机版 --> 25 <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> 26 <constructor-arg name="host" value="47.100.101.31" /> 27 <constructor-arg name="port" value="6379" /> 28 <constructor-arg name="poolConfig" ref="jedisPoolConfig" /> 29 </bean> 30 <!-- Jedis集群版 --> 31 <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> 32 <constructor-arg name="nodes"> 33 <set> 34 <bean class="redis.clients.jedis.HostAndPort"> 35 <constructor-arg name="host" value="47.100.101.31"/> 36 <constructor-arg name="port" value="7001"/> 37 </bean> 38 <bean class="redis.clients.jedis.HostAndPort"> 39 <constructor-arg name="host" value="47.100.101.31"/> 40 <constructor-arg name="port" value="7002"/> 41 </bean> 42 <bean class="redis.clients.jedis.HostAndPort"> 43 <constructor-arg name="host" value="47.100.101.31"/> 44 <constructor-arg name="port" value="7003"/> 45 </bean> 46 <bean class="redis.clients.jedis.HostAndPort"> 47 <constructor-arg name="host" value="47.100.101.31"/> 48 <constructor-arg name="port" value="7004"/> 49 </bean> 50 <bean class="redis.clients.jedis.HostAndPort"> 51 <constructor-arg name="host" value="47.100.101.31"/> 52 <constructor-arg name="port" value="7005"/> 53 </bean> 54 <bean class="redis.clients.jedis.HostAndPort"> 55 <constructor-arg name="host" value="47.100.101.31"/> 56 <constructor-arg name="port" value="7006"/> 57 </bean> 58 </set> 59 </constructor-arg> 60 <constructor-arg name="poolConfig" ref="jedisPoolConfig"/> 61 </bean>
使用集群请注释掉单机版的
最后还有点小bug,7001节点不能出现在jediscluster的构造函数中,不然访问7001节点内容会超时,原因未知。我用3.2.9重装一遍没发现任何问题,待大佬们解决吧
1 public interface JedisClient { 2 3 String get(String key); 4 5 String set(String key, String value); 6 7 long ttl(String key); 8 9 long expire(String key, int second); 10 11 long incr(String key); 12 13 long hset(String hkey, String key, String value); 14 15 String hget(String hkey, String key); 16 17 long del(String key); 18 19 long hdel(String hkey, String key); 20 }
1 public class JedisClientCluster implements JedisClient { 2 3 @Resource 4 private JedisCluster jedisCluster; 5 6 @Override 7 public String get(String key) { 8 return jedisCluster.get(key); 9 } 10 11 @Override 12 public String set(String key, String value) { 13 return jedisCluster.set(key, value); 14 } 15 16 @Override 17 public long ttl(String key) { 18 return jedisCluster.ttl(key); 19 } 20 21 @Override 22 public long expire(String key, int second) { 23 return jedisCluster.expire(key, second); 24 } 25 26 @Override 27 public long incr(String key) { 28 return jedisCluster.incr(key); 29 } 30 31 @Override 32 public long hset(String hkey, String key, String value) { 33 return jedisCluster.hset(hkey, key, value); 34 } 35 36 @Override 37 public String hget(String hkey, String key) { 38 return jedisCluster.hget(hkey, key); 39 } 40 41 @Override 42 public long del(String key) { 43 return jedisCluster.del(key); 44 } 45 46 @Override 47 public long hdel(String hkey, String key) { 48 return jedisCluster.del(hkey, key); 49 } 50 }
1 public class JedisClientSingle implements JedisClient { 2 3 @Resource 4 private JedisPool jedisPool; 5 6 @Override 7 public String get(String key) { 8 Jedis jedis = jedisPool.getResource(); 9 String string = jedis.get(key); 10 jedis.close(); 11 return string; 12 } 13 14 @Override 15 public String set(String key, String value) { 16 Jedis jedis = jedisPool.getResource(); 17 String string = jedis.set(key, value); 18 jedis.close(); 19 return string; 20 } 21 22 @Override 23 public String hget(String hkey, String key) { 24 Jedis jedis = jedisPool.getResource(); 25 String string = jedis.hget(hkey, key); 26 jedis.close(); 27 return string; 28 } 29 30 @Override 31 public long hset(String hkey, String key, String value) { 32 Jedis jedis = jedisPool.getResource(); 33 Long result = jedis.hset(hkey, key, value); 34 jedis.close(); 35 return result; 36 } 37 38 @Override 39 public long incr(String key) { 40 Jedis jedis = jedisPool.getResource(); 41 Long result = jedis.incr(key); 42 jedis.close(); 43 return result; 44 } 45 46 @Override 47 public long expire(String key, int second) { 48 Jedis jedis = jedisPool.getResource(); 49 Long result = jedis.expire(key, second); 50 jedis.close(); 51 return result; 52 } 53 54 @Override 55 public long ttl(String key) { 56 Jedis jedis = jedisPool.getResource(); 57 Long result = jedis.ttl(key); 58 jedis.close(); 59 return result; 60 } 61 62 @Override 63 public long del(String key) { 64 Jedis jedis = jedisPool.getResource(); 65 Long result = jedis.del(key); 66 jedis.close(); 67 return result; 68 } 69 70 @Override 71 public long hdel(String hkey, String key) { 72 Jedis jedis = jedisPool.getResource(); 73 Long result = jedis.hdel(hkey, key); 74 jedis.close(); 75 return result; 76 } 77 }
如果集群版和主从复制版想要共存,那么就在spring配置文件中手动注入