redis集群中的所有节点彼此互联,节点内部采用二进制协议优化传输速度和带宽,每个节点都可以与Java客户端连接。redis集群的数据分配采用哈希槽,它内置了16384个哈希槽,开发者可以根据每个redis实例的性能来调整每个redis实例上哈希槽的分布范围。当需要进行数据存取时,redis首先使用CRC16算法对key进行计算,计算结果对16384取余,即CRC16(key)%16384,使用余数来确定去哪一个节点存取这个key。当集群中超过半数的节点检测失效时会认为该节点失效。
1.集群规划
本次在两台服务器上进行安装,192.168.72.129和192.168.72.130,集群规划如下:
主节点:192.168.72.129:7001,192.168.72.129:7002,192.168.72.129:7003
从节点:192.168.72.130:8001,192.168.72.130:8002,192.168.72.130:8003
2.安装Ruby环境
Redis5.0之前的版本,创建集群需要使用redis集群管理工具redis-trib.rb
,它依赖于Ruby环境,需要安装Ruby环境,5.0之后的版本不再依赖Ruby环境,请自行绕过该小节。
为了方便安装,此处使用Ruby版本管理工具rvm来进行安装(可参考rvm官网:http://www.rvm.io/)。
安装GPG key:
gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
安装rvm:
\curl -sSL https://get.rvm.io | bash -s stable
此时发现有报错:
这是因为nss版本过低导致,升级nss版本:yum -y update nss
,再次执行上一步命令安装成功。
查找rvm配置文件: find / -name rvm.sh
,返回路径为:/etc/profile.d/rvm.sh。
使配置文件生效:source /etc/profile.d/rvm.sh
下载rvm依赖:rvm requirements
查看rvm库Ruby版本:rvm list known
,如下图所示:
选择指定版本进行安装:rvm install ruby-2.5.3
安装后有提示:
查看/usr/local/rvm/log/1551880376_ruby-2.5.3/rubygems.install.log,里边有如下提示:
此时需要使用rvm install ruby-2.5.3 --rubygems ignore
命令来安装。安装成功:
设置ruby默认版本:rvm use 2.5.3 default
最后,安装Redis依赖:gem install redis
3.解压安装redis
两台服务器上均执行如下操作:
(1)上传下载好的redis安装包redis-5.0.3.tar.gz到/instal
目录下,执行命令tar -zxvf redis-5.0.3.tar.gz
解压文件;
(2)将解压好的文件复制到规划好的安装位置cp -r redis-5.0.3 /usr/local/redis_cluster
;
(3)在/usr/local/redis_cluster
目录下执行以下命令进行安装:
make MALLOC=libc
make install
安装期间会遇到的问题可参考:https://blog.csdn.net/m0_37674755/article/details/88094197 编译安装部分。
4.配置集群
两台服务器上均执行如下操作:
(1)在/usr/local/redis_cluster
下创建cluster_config
文件夹:mkdir cluster_config
;
(2)主节点cluster_config目录下创建7001,7002,7003
三个文件夹,从节点cluster_config下面创建8001,8002,8003
三个文件夹,用于存放各redis节点的配置文件。
(3)将集群安装目录/usr/local/redis_cluster
下的redis.conf
分别向cluster_config下的7001,7002,7003,8001,8002,8003
文件夹中复制一份。
(4)修改刚才复制好的redis.conf文件,主要修改如下配置(以7001节点为例):
port 7001
:按照集群规划修改为各自监听的端口号:
cluster-enabled yes
:开启集群
cluster-config-file nodes-7001.conf
:集群节点的配置文件
bind 0.0.0.0
:绑定主机地址,修改为0.0.0.0表示允许外部访问
daemonize yes
:允许后台运行
requirepass 123456
:开启密码认证,密码为123456
masterauth 123456
:从机登录主机需要认证,密码123456
protected-mode no
:已经设置了密码认证,此处关闭保护模式
(5)6个节点均配置完成后,分别进入到两台服务器的/usr/local/redis_cluster/
目录下,分别启动6个redis实例:
redis-server ./cluster_config/7001/redis.conf
redis-server ./cluster_config/7002/redis.conf
redis-server ./cluster_config/7003/redis.conf
redis-server ./cluster_config/8001/redis.conf
redis-server ./cluster_config/8002/redis.conf
redis-server ./cluster_config/8003/redis.conf
检查redis实例是否启动:ps -ef|grep 700
, ps -ef|grep 800
(6)执行命令创建Redis集群
redis5.0之前的版本使用redis-trib.rb创建,执行如下命令:
cd /usr/local/redis_cluster/src/
cp redis-trib.rb ../cluster_config/
redis-trib.rb create --replicas 1 192.168.72.129:7001 192.168.72.129:7002 192.168.72.129:7003 192.168.72.130:8001 192.168.72.130:8002 192.168.72.130:8003
redis5.0之后的版本使用redis-cli方式创建,执行如下命令:
cd /usr/local/redis_cluster/src/
./redis-cli --cluster create --cluster-replicas 1 -a 123456 192.168.72.129:7001 192.168.72.129:7002 192.168.72.129:7003 192.168.72.130:8001 192.168.72.130:8002 192.168.72.130:8003
其中cluster-replicas 1表示每个主节点的slave数量,同时由于开启了密码认证,所以携带参数-a 123456进行认证,此时可能有报错:
这是因为两台机器之间redis实例的端口不通的缘故,设置防火墙,开放每个redis实例的端口7001,7002,7003,8001,8002,8003,同时还应该开启集群总线端口,集群总线的端口是redis实例端口号上加10000,即17001,17002,17003,18001,18002,18003然后再次执行上面的命令,出现如下提示,输入yes后创建成功。
从成功的提示信息可以看到,使用6个节点搭建的集群,redis自己分配了三个节点作为主节点,另外三个作为从节点,如id为5bca5aa06864b3fe60b191c4a1ecb638d3abc7d1的slave实例对应的master节点id为94cab328ab66b4f04f6bfcb3d67880538cbead83。
注:有时候会碰到输完yes后提示“Waiting for the cluster to join”,然后一直等待的情况,这时候首先应该检查集群总线端口是否开通,如果已经开通,可以分别登录各个节点,输入meet <本实例ip> <本实例端口>来尝试。
5.测试集群
(1)测试集群存放数据:
随便登录一个节点的实例,如登录7001这个端口监听的实例,使用命令:redis-cli -a 123456 -p 7001 -c
登录。
执行set name zhangsan
存入一个key=name,value=zhangsan的字符串:
可以看到,name这个key的计算后取余数为5798,故而name这个key存入了5798这个槽位,对应8001这个节点。
(2)测试查询数据
重新登录另外一个节点,如7003节点:redis-cli -a 123456 -p 7003 -c
,执行查询命令:get name
可以看到,获取数据的时候,redis也是对name这个key进行计算,最终结果为5798这个槽位号,对应于8001这个节点,从该节点获取数据来展示。
通过以上两个存放,查询数据的实例,我们可以很清楚地看到,redis集群的工作原理,它不同于redis单点,redis主从那样将全部数据放到一个实例中,redis集群中的数据是分片散布在各个节点上的,当发送数据存取命令时,redis按照计算结果确定对哪一个槽位号对应的节点进行操作。集群分散了redis的压力,节点互通保证了数据共享。
(3)测试某一个主节点宕机的情况
如有redis集群中的两个节点,使用info replication
命令查看7003为master节点,而8001节点为其slave节点:
7003节点:
8001节点:
然后获取已经存放在7003节点的数据:
这时候我们停止7003这个master节点,然后登录集群中的另外一个几点,继续获取该值,同样会有返回值123456,这时候登录8001节点,查看主从状态:
发现8001节点已经由原先的slave变成了master,这时候再启动7003节点,然后再查看8001节点的主从状态,发现多了一个salve节点。
由此我们可以看出来,当集群中的某个master节点宕机的时候,如果继续发生对该节点的存取数据操作,其对应的salve节点会主动切换为master节点,来保证业务进行。当之前宕机的master节点恢复后,它会主动作为现在master节点的slave。
1.添加依赖
redis.clients
jedis
org.springframework.data
spring-data-redis
2.0.9.RELEASE
org.apache.commons
commons-pool2
2.5.0
2.配置集群信息
在application.yml中进行如下配置:
#redis集群配置
redis:
cluster:
ports_1:
- 7001
- 7002
- 7003
ports_2:
- 8001
- 8002
- 8003
host_1: 192.168.72.129
host_2: 192.168.72.130
password: 123456
poolConfig:
max-total: 8
max-idle: 8
max-waitmillis: -1
min-idle: 0
3.编写redis集群配置类
package com.fix.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ArrayList;
import java.util.List;
/**
* redis集群配置
*/
@Configuration
@ConfigurationProperties(prefix = "redis.cluster")
public class RedisClusterConfig {
private List ports_1;
private List ports_2;
private String host_1;
private String host_2;
private String password;
private JedisPoolConfig poolConfig;
@Bean
public RedisClusterConfiguration getRedisClusterConfiguration(){
RedisClusterConfiguration configuration=new RedisClusterConfiguration();
configuration.setPassword(RedisPassword.of(password));
List nodes=new ArrayList<>();
for (Integer integer : ports_1) {
nodes.add(new RedisNode(host_1,integer));
}
for (Integer integer : ports_2) {
nodes.add(new RedisNode(host_2,integer));
}
configuration.setClusterNodes(nodes);
return configuration;
}
/**
* 配置使用rediscluster配置jedis连接工厂
* @return
*/
@Bean
public JedisConnectionFactory getJedisConnectionFactory(){
JedisConnectionFactory factory=new JedisConnectionFactory(getRedisClusterConfiguration(),poolConfig);
return factory;
}
/**
* 配置RedisTemplate的key,value的序列化方式
*/
@Bean
public RedisTemplate getRedisTemplate(){
RedisTemplate redisTemplate=new RedisTemplate();
redisTemplate.setConnectionFactory(getJedisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
/**
* 配置StringRedisTemplate的key,value序列化方式
*/
@Bean
public StringRedisTemplate getStringRedisTemplate(){
StringRedisTemplate stringRedisTemplate=new StringRedisTemplate(getJedisConnectionFactory());
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new StringRedisSerializer());
return stringRedisTemplate;
}
//getter ,setter
}
4.编写controller测试
创建User实体类,需要实现Serializable 接口:
public class User implements Serializable {
private Long id;
private String name;
private Byte sex;
private String phone;
private Byte age;
private Boolean status;
private String address;
//getter,setter
}
创建RedisTestController:
@RestController
@RequestMapping("/test/redis")
public class RedisTestController {
@Autowired
RedisTemplate redisTemplate;
@Autowired
StringRedisTemplate stringRedisTemplate;
@GetMapping("/cluster")
public void testCluster(){
//stringRedisTemplate存取值
ValueOperations stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set("testKey","testVlaue");
System.out.println(stringStringValueOperations.get("testKey"));
//redisTemplate存取值
User user=new User();
user.setId(1L);
user.setName("张三");
user.setSex((byte)3);
user.setAge((byte)20);
user.setPhone("111111111");
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("user",user);
System.out.println(valueOperations.get("user"));
}
}
启动springboot应用,访问 https://localhost:8081/test/redis/cluster , 查看控制台输出日志:
说明springboot整合redis已经成功。