Redis3.0 及其之后的版本提供了redis-cluster
集群支持,用于在多个redis节点间共享数据,以提高服务的可用性。
构建 redis-cluster 集群可以通过 redis-trib.rb 工具来完成。redis-trib.rb 是redis官方提供的一个集群管理工具,集成在redis安装包的 src 目录下。redis-trib.rb 封装了redis提供的集群命令,使用简单、便捷。
因为 redis-trib.rb 是由ruby语言编写的,所以使用该工具需要ruby语言环境的支持。
1 2
| $ ruby -v ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]
|
redis-cluster集群
1、配置
要启用redis-cluster集群,需要先修改redis配置文件集群配置部分的内容
redis.conf
1 2 3 4 5 6 7 8 9 10 11
| # # 启用redis-cluster集群 cluster-enabled yes # 集群节点配置文件 # 该文件无需手工修改,由redis自动维护(创建和更新) # 需要注意,单机运行多实例时,确保该文件没有被其他实例覆盖(不允许重名) cluster-config-file nodes-6377.conf # 节点超时时长(毫秒) cluster-node-timeout 150001234567891011
|
为了方便进行演示,这里分别以端口 6377、6378、6379 各启用一个实例来代表不同的redis服务器
2、创建集群
创建集群使用 redis-trib 的 create
命令完成,create
命令的格式为:
1
| create host1:port1 ... hostN:portN1
|
默认情况下,ruby 是无法识别redis的,直接执行 redis-trib.rb create IP:PORT
将会报错
1 2 3 4
| $ ./redis-trib.rb create 192.168.206.128:6377 192.168.206.128:6378 192.168.206.128:6379 /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- redis (LoadError) from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require' from ./redis-trib.rb:25:in `<main>'1234
|
所以需要先为ruby安装redis第三方接口,执行命令 gem install redis
即可
1 2 3 4 5 6 7 8
| $ sudo gem install redis [sudo] password for zhangcs: Fetching: redis-4.0.1.gem (100%) Successfully installed redis-4.0.1 Parsing documentation for redis-4.0.1 Installing ri documentation for redis-4.0.1 Done installing documentation for redis after 1 seconds 1 gem installed12345678
|
此时再使用 create
就可以将6377、6378、6379 这3台服务器构建成一个集群了
有一点需要注意的是,redis-cluster集群至少需要3个可用节点。
3、查看集群
使用 info
命令指定集群上任一节点的地址便可以查看集群状态
主从复制模型
刚才说到,redis-cluster至少需要保证3个节点可用。那么为了避免节点宕机导致服务不可用,我们就需要添加主从配置,为集群中的节点增加从节点;使其在主节点宕机时,能够将从节点提升为主节点代替原来的主节点进行工作。
在非集群的单节点环境中,配置主从关系的方法大致有 2 种:
1、修改从服务器配置文件 redis.conf 的 slaveof <masterip> <masterport>
选项;
2、在从服务器上使用slaveof
命令直接指定主服务器。
然而在redis-cluster集群环境中,启用了cluster配置的情况下slaveof
是不可用的。
假定有 6381 服务器配置 slaveof
指定主服务器 6377 ,同时该服务器启用了redis-cluster配置
1 2 3 4 5 6 7
| # slaveof 192.168.206.128 6377 # cluster-enabled yes cluster-config-file nodes-6381.conf cluster-node-timeout 150001234567
|
那么此时 6381 端口的服务器将无法成功启动
1 2 3 4 5
| $ ./redis-server /usr/local/redis/6381/redis.conf *** FATAL CONFIG FILE ERROR *** Reading the configuration file, at line 283 >>> 'slaveof 192.168.206.128 6377' slaveof directive not allowed in cluster mode12345
|
显然我们无法使用原有的主从配置方法对集群进行配置。此时我们需要借助于 redis-trib.rb 工具来进行从节点的添加操作,先将节点加入集群中,然后再声明节点为原有节点的从节点。
1、启用服务后先将该节点加入集群,直接使用redis-trib.rb的 add-node
命令即可实现:
1
| redis-trib add-node new_host:new_port existing_host:existing_port1
|
此时通过 redis-trib info
能够查看到该节点已经成功加入了集群中,并且该节点并没有分配哈希槽
2、声明该节点为集群中某一节点的从节点,需要使用客户端进入该节点(此处即为新增的从节点 6381)进行设置,使用 cluster replicate
命令指定所属主节点ID。
主节点ID可以使用客户端连接到集群后通过命令 cluster nodes
查看 :
使用客户端连接新增的从节点 6381 ,指定主节点 6377 在集群中的ID ,声明为 6377 节点的从节点
1 2 3
| $ ./redis-cli -h 192.168.206.128 -c -p 6381 192.168.206.128:6381> cluster replicate e10dde558fb46fe8ae6fe66e54ef56032fbcce0f OK
|
至此就完成了集群中一个节点的主从配置,查看 6377 节点能够看到其包含一个从节点:
查询出 6377 端口服务对应的PID,然后通过 kill 将服务关闭,使该节点在集群上不可用;此时查看集群信息,能够发现从节点 6381 自动提升为主节点,顶替了不可用的 6377 节点。
重新启动已宕机的服务后,该节点将会被当做从节点添加到管理原先的哈希槽分配范围的节点上。这里也就是添加到了 6381 节点上,6381 节点管理的哈希槽就是原先由 6377 节点所管理的
客户端连接 redis-cluster
客户端在连接 redis 服务器时带上参数 -c 即为连接到cluster集群
1 2 3 4 5
| $ redis-cli -h 192.168.206.128 -c -p 6377 192.168.206.128:6377> set name zhangcs -> Redirected to slot [5798] located at 192.168.206.128:6378 OK 192.168.206.128:6378>12345
|
可以看到,在 6377 端口的服务器上存储一个string类型的键值对 name = zhangsan
的时,操作被重定向到了 6378 端口的服务器上,而 name = zhangsan
这个键值对最终也被存储在了 6378 端口的服务器里。
同理,在获取数据时,也会重定向到对应数据实际存储的服务器上,然后在该服务器上进行操作。
1 2 3 4
| $ ./redis-cli -h 192.168.206.128 -c -p 6377 192.168.206.128:6378> get name "zhangcs" 192.168.206.128:6378>1234
|
单独连接集群上的节点
需要注意的是,节点在加入集群后,如果不声明参数 -c 连接集群而是单独连接集群上的节点,那么在操作时如果需要重定向到其他的服务器,是无法成功重定向然后完成操作的。
例如键值对 name = zhangsan
存储在 6378 端口的服务器上,此时如果我们单独连接到 6377 端口的服务器上进行操作,那么该操作是无法成功的。
1 2 3 4 5 6
| $ redis-cli -h 127.0.0.1 -p 6377 127.0.0.1:6377> get name (error) MOVED 5798 127.0.0.1:6378 127.0.0.1:6377> set name lisi (error) MOVED 5798 127.0.0.1:6378 127.0.0.1:6377>123456
|
而如果无需重定向,则能成功完成操作。
1 2 3 4
| $ redis-cli -h 127.0.0.1 -p 6378 127.0.0.1:6378> get name "zhangsan" 127.0.0.1:6378>1234
|
Spring集成 redis-cluster
1、连接池配置
1 2 3 4 5 6 7 8
| @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(100); jedisPoolConfig.setMaxIdle(10); jedisPoolConfig.setMaxWaitMillis(1500); return jedisPoolConfig; }
|
2、JedisCluster对象配置
1 2 3 4 5 6 7 8 9 10
| @Bean public JedisCluster jedisCluster() { Set<HostAndPort> nodes = new HashSet<HostAndPort>(); nodes.add(new HostAndPort("192.168.206.128", 6377)); nodes.add(new HostAndPort("192.168.206.128", 6378)); nodes.add(new HostAndPort("192.168.206.128", 6379)); nodes.add(new HostAndPort("192.168.206.128", 6381)); JedisCluster jedisCluster = new JedisCluster(nodes, jedisPoolConfig()); return jedisCluster; }
|
3、JedisCluster对象的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Autowired private JedisCluster jedisCluster; @Test public void testCluster() { Assert.assertNotNull(jedisCluster); String result = jedisCluster.set("name", "zhangsan"); Assert.assertNotNull(result); Assert.assertEquals("OK", result); String name = jedisCluster.get("name"); Assert.assertNotNull(name); Assert.assertEquals("zhangsan", name); }
|
spring-cache集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| @Configuration @ComponentScan(basePackages = {"org.pro.service"}) @EnableCaching public class RootConfig { @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(1000); jedisPoolConfig.setMaxIdle(10); jedisPoolConfig.setMaxWaitMillis(1500); return jedisPoolConfig; } @Bean public RedisClusterConfiguration redisClusterConfiguration() { RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); Set<RedisNode> nodes = new HashSet<>(); nodes.add( new RedisNode("127.0.0.1", 6377)); nodes.add( new RedisNode("127.0.0.1", 6378)); nodes.add( new RedisNode("127.0.0.1", 6379)); redisClusterConfiguration.setClusterNodes(nodes); redisClusterConfiguration.setMaxRedirects(4); return redisClusterConfiguration; } @Bean public JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration(), jedisPoolConfig()); jedisConnectionFactory.setTimeout(15000); return jedisConnectionFactory; } @Bean public RedisTemplate redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory()); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(stringRedisSerializer); return redisTemplate; } @Bean public RedisCacheManager redisCacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate()); return redisCacheManager; } }
|