redis高可用需要保证,在主节点崩溃的时候,从节点能够成为主节点,继续提供服务。默认来说主从模式master-slave就能做到这一点,但是在实际环境中,客户端连接的是指定的主机和端口,一旦master奔溃,那么这个节点就无法连接了,需要人为设置连接信息为从节点的主机和端口。redis高可用解决方案sentinel模式解决了需要手动设置从节点主机和端口的问题,sentinel模式简称哨兵模式,当主节点奔溃的时候,哨兵节点会通过选举的方式选举出一个从节点来作为主节点继续提供redis服务。可以说sentinel哨兵模式就是在主从模式的基础上,增加了哨兵角色,来监控系统的状态,系统异常出现的时候,这些哨兵角色他们就发挥作用了。
搭建sentinel模式环境,需要先搭建一个主从模式,搭建一个最小的sentinel模式,需要一个master,一个slave节点,三个sentinel哨兵节点。结构图如下所示:
搭建过程需要先启动主从节点,然后启动sentinel节点。这里以一台虚拟机为例,我们启动五个进程来搭建redis sentinel模式,安装过redis-4.0.2之后,安装目录下回自动生成redis.conf sentinel.conf,我们可以拷贝复制这两个配置文件到/data/redis/conf目录下,然后组成我们这个系统的配置文件。
我们对默认的配置文件稍作修改,设置对应的端口号,指定对应的dir和logfile、pidfile
redis.conf
bind 0.0.0.0
protected-mode yes
port 6379
daemonize yes
pidfile "/var/run/redis_6379.pid"
logfile "/data/redis/logs/redis_6379.log"
dir "/data/redis/data/6379"
slave.conf
bind 0.0.0.0
protected-mode yes
port 6380
daemonize yes
pidfile "/var/run/redis_6380.pid"
logfile "/data/redis/logs/redis_6380.log"
dir "/data/redis/data/6380"
sentinel-26379.conf
port 26379
daemonize yes
protected-mode no
dir "/data/redis/data/26379"
logfile "/data/redis/logs/redis_26379.log"
sentinel monitor mymaster 10.119.9.149 6379 2
sentinel-26380.conf
port 26380
daemonize yes
protected-mode no
dir "/data/redis/data/26380"
logfile "/data/redis/logs/redis_26380.log"
sentinel monitor mymaster 10.119.9.149 6379 2
sentinel-26381.conf
port 26381
daemonize yes
protected-mode no
dir "/data/redis/data/26381"
logfile "/data/redis/logs/redis_26381.log"
sentinel monitor mymaster 10.119.9.149 6379 2
下面可以启动主从节点,并通过命令的方式构建一个master-slave模式。
主从复制构建完成,我们验证主从复制:先在主节点上保存数据,然后在从节点上查看数据。
从节点查看数据:
主从复制环境搭建完成,我们只需要启动哨兵节点即可。
故障测试,人为停止6379端口的进程,然后看看主节点是否转移。
故障前:
停止6379进程:
故障转移之后:
从这里可以看出,主节点发生了改变,当6379端口服务停止之后,6380端口转为了master,继续提供redis连接服务。
sentinel哨兵模式这种高可用也是redis官方推荐的,下面通过java来调用jedis提供的api来操作sentinel集群。下面是一个简单的代码:
package com.xxx.service;
import java.util.HashSet;
import java.util.Set;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
public class JedisSentinels {
public static JedisSentinelPool jedisSentinelPool;
private static final String master="mymaster";
private static Set sentinels = new HashSet<>();
static {
sentinels.add("10.119.9.149:26379");
sentinels.add("10.119.9.149:26380");
sentinels.add("10.119.9.149:26381");
jedisSentinelPool = new JedisSentinelPool(master, sentinels);
}
public static void set(String key,String value) {
Jedis jedis = jedisSentinelPool.getResource();
try {
jedis.set(key.getBytes(), value.getBytes());
} finally {
jedis.close();
}
}
public static byte[] get(String key) {
Jedis jedis = jedisSentinelPool.getResource();
byte[] bs;
try {
bs = jedis.get(key.getBytes());
} finally {
jedis.close();
}
return bs;
}
public static void main(String[] args) {
String value = new String(get("name"));
System.out.println(value);
//set("name", "sentinel");
}
}
这个实例中,我们通过打印redis中key为name的value。运行打印结果如下:
代码中,我们使用的是JedisSentinelPool来构建redis连接池,他需要传入一个masterName和sentinels集合作为参数,sentinels集合就是我们搭建环境中的sentinel角色的主机和端口集合,因此当系统出现故障的时候,我们无需手动修改代码,当sentinel哨兵角色选举出新的主节点之后,该系统仍然可以继续服务。调用者对于系统故障无感知。 这就是为什么redis官方也推荐这种集群方式的原因。