故障转移的基本原理:
也支持多个master-slave结构:
本地安装的结构图:
对于master:redis-7000.conf配置:
port 7000
daemonize yes
pidfile /usr/local/redis/data/redis-7000.pid
logfile "7000.log"
dir "/usr/local/redis/data"
对于slave:redis-7001和redis-7002配置:
port 7001
daemonize yes
pidfile /usr/local/redis/data/redis-7001.pid
logfile "7001.log"
dir "/usr/local/redis/data"
slaveof 127.0.0.1 7000
启动redis服务:
redis-server ../config/redis-7000.conf
访问7000端口的master redis:
redis-cli -p 7000 info replication
显示他有两个从节点:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7002,state=online,offset=99550,lag=1
slave1:ip=127.0.0.1,port=7001,state=online,offset=99816,lag=0
master_repl_offset:99816
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:99815
对于sentinel主要配置:
master sentinel config:
port 26379
daemonize yes
dir "/usr/local/redis/data"
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
...
启动redis sentinel:
redis-sentinel ../config/redis-sentinel-26379.conf
访问26379 redis sentinel master:
redis-cli -p 26379 info sentinel
显示:
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
查看这六个进程是否都起来了:ps -ef | grep redis
注意,如果上面是配置在虚拟机的话,需要将127.0.0.1改为虚拟机的ip,要不然找不着。
JedisSentinelPool只是一个配置中心,不需要具体连接某个redis,注意它不是代理。
private Logger logger = LoggerFactory.getLogger(AppTest.class);
@Test
public void test4(){
//哨兵配置,我们访问redis,就通过sentinel来访问
String masername = "mymaster";
Set sentinels = new HashSet<>();
sentinels.add("10.128.24.176:26379");
sentinels.add("10.128.24.176:26380");
sentinels.add("10.128.24.176:26381");
JedisSentinelPool sentinelPool = new JedisSentinelPool(masername,sentinels);
//一个while死循环,每隔一秒往master塞入一个值,并且日志打印
while (true){
Jedis jedis = null;
try{
jedis = sentinelPool.getResource();
int index = new Random().nextInt(100000);
String key = "k-" + index;
String value = "v-" + index;
jedis.set(key,value);
logger.info("{} value is {}",key,jedis.get(key));
TimeUnit.MILLISECONDS.sleep(1000);
}catch (Exception e){
logger.error(e.getMessage(),e);
}finally {
if(jedis != null){
jedis.close();
}
}
}
}
maven依赖是:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.6version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.1.1version>
dependency>
启动程序,发现是正常写入:
16:16:01.424 [main] INFO com.njupt.swg.AppTest - k-54795 value is v-54795
16:16:02.426 [main] INFO com.njupt.swg.AppTest - k-55630 value is v-55630
16:16:03.429 [main] INFO com.njupt.swg.AppTest - k-70642 value is v-70642
16:16:04.430 [main] INFO com.njupt.swg.AppTest - k-42978 value is v-42978
16:16:05.431 [main] INFO com.njupt.swg.AppTest - k-96297 value is v-96297
16:16:06.433 [main] INFO com.njupt.swg.AppTest - k-4220 value is v-4220
16:16:07.435 [main] INFO com.njupt.swg.AppTest - k-34103 value is v-34103
16:16:08.436 [main] INFO com.njupt.swg.AppTest - k-9177 value is v-9177
16:16:09.437 [main] INFO com.njupt.swg.AppTest - k-24389 value is v-24389
16:16:10.439 [main] INFO com.njupt.swg.AppTest - k-32325 value is v-32325
16:16:11.440 [main] INFO com.njupt.swg.AppTest - k-68538 value is v-68538
16:16:12.441 [main] INFO com.njupt.swg.AppTest - k-36233 value is v-36233
16:16:13.443 [main] INFO com.njupt.swg.AppTest - k-305 value is v-305
16:16:14.444 [main] INFO com.njupt.swg.AppTest - k-59279 value is v-59279
我们将现在的端口为7000的redis master 给kill掉
kill -9 master的pid
我们会发现:客户端报异常,但是在大概十几秒之后,就继续正常塞值了。原因是服务端的哨兵机制的选举matser需要一定的时间。
对于之前的Sentinel配置文件中有两条配置:
监控master redis节点,这里是当超过两个sentinel认为master挂了,则认为master挂了。
sentinel monitor
sentinel monitor mymaster 127.0.0.1 6379 2
这里是每秒sentinel都回去Ping周围的master redis,超过30秒没有任何相应,说明其挂了。
sentinel down-after-milliseconds
sentinel down-after-milliseconds mymaster 300000
主观下线:每个sentinel节点对Redis节点失败的“偏见”
这是一种主观下线。因为在复杂的网络环境下,这个sentinel与这个master不通,但是master与其他的sentinel都是通的呢?所以是一种“偏见”
这是依靠的第三种定时:每秒去ping一下周围的sentinel和redis。对于slave redis,可以使用这个主观下线,因为他不需要进行故障转移。
客观下线:所有sentinel节点对master Redis节点失败“达成共识”(超过quorum个则统一)
这是依靠的第二种定时:每两秒,sentinel之间进行“商量”,传递的消息是:sentinel is-master-down-by-addr
对于master redis的下线,必须要达成共识才可以,因为涉及故障转移,仅仅依靠一个sentinel判断是不够的。
原因:只有一个sentinel节点完成故障转移
选举:通过sentinel is-master-down-by-addr命令都希望成为领导者
那么,如何选择“合适”的slave节点呢?
主节点下线:sentinel failover
从节点下线要注意读写分离问题。
redis sentinel是redis高可用实现方案:故障发现、故障自动转移、配置中心、客户端通知。
redis sentinel从redis2.8版本才正式生产可用,之前版本不可生产用。
尽可能在不同物理机上部署redis sentinel所有节点。
redis sentinel中的sentinel节点个数应该大于等于3且最好是奇数。
redis sentinel中的数据节点和普通数据节点没有区别。每个sentinel节点在本质上还是一个redis实例,只不过和redis数据节点不同的是,其主要作用是监控redis数据节点
客户端初始化时连接的是sentinel节点集合,不再是具体的redis节点,但sentinel只是配置中心不是代理。
redis sentinel通过三个定时任务实现了sentinel节点对于主节点、从节点、其余sentinel节点的监控。
redis sentinel在对节点做失败判定时分为主观下线和客观下线。
看懂redis sentinel故障转移日志对于redis sentinel以及问题排查非常有用。
redis sentinel实现读写分离高可用可以依赖sentinel节点的消息通知,获取redis数据节点的状态变化。
redis sentinel可以实现高可用的读写分离,高可用体现在故障转移,那么实现高可用的基础就是要有从节点,主从节点还实现了读写分离,减少master的压力。但是如果是从节点下线了,sentinel是不会对其进行故障转移的,并且连接从节点的客户端也无法获取到新的可用从节点,而这些问题在Cluster中都得到了有效的解决。
对于性能提高、容量扩展的时候,这种方式是比较复杂的,比较推荐的是使用集群,就是下面讨论的redis cluster!