Redis Sentinel是Redis高可用的实现方案,在实际生产环境中,对提高整个系统的高可用性是非常有帮助的。
当主节点发生故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知客户端,从而实现高可用。
Sentinel架构
在主从的基础上增加Sentinel节点对其进行监控以及故障转移。Sentinel并不会改变原有的主从结构,只是在主从的基础上新增一个监控集群,也就是sentinel集群。一个sentinel集群可以监控多个主从集群。
故障转移步骤
- 主节点故障,从节点失去连接
- sentinel发现主节点故障
- 选举处理故障的sentinel节点
- 故障转移
- slaveof no one
- notice client
- slaveof new master
环境搭建
- 服务器规划
ip | 角色 |
---|---|
192.168.0.51 | master |
192.168.0.52 | slave |
192.168.0.53 | slave |
192.168.0.41 | sentinel-1 |
192.168.0.42 | sentinel-2 |
192.168.0.43 | sentinel-3 |
配置说明
- port 端口号
- logfile 日志文件的存放目录
- daemonize 是否以守护进程的方式运行
- sentinel monitor master_name ip port quonum
- sentinel down-after-milliseconds master_name times 失联的超时时间
- sentinel parallel-syncs master_name n 并发复制的数量
- sentinel failover-timeout master_name times 故障转移超时时间
- sentinel auth-pass master_name passwd 主节点的密码
- sentinel notification-script master_name script_path 故障转移期间时触发执行的脚本
- sentinel client-reconfig-script master_name script_path 故障转移后执行的脚本
实现原理
- 主观下线(sdown)和客观下线(odown)
- sentinel选举
- 故障转移的过程(master的选举)
客户端API
- sentinel masters master_name 主节点的状态及相关信息
- sentinel slaves master_name 从节点的状态及相关信息
- sentinel sentinels master_name sentinel节点的集合信息
- sentinel get-master-addr-by-name master_name 主节点的IP地址和端口
- sentinel reset pattern 对指定主节点的配置进行重置
- sentinel failover master_name 对指定主节点强行进行故障转移
- sentinel ckqourum master_name 检测当前可达的sentinel节点总数是否到达quorum的个数
- sentinel flushconfig 将sentinel节点的配置同步到磁盘上
- sentinel remove master_name 取消对指定主节点的监控
- sentinel monitor master_name id port quorum 监控指定主节点
- sentinel set master_name 动态修改sentinel节点配置
Java连接Sentinel
- 使用
package stu.redis.sentinel;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
/**
* jedis 操作哨兵
*
* @author worker
*
*/
public class RedisSentinel {
/**
* 主节点名字
*/
private static final String MASTER_NAME = "mymaster";
public static void main(String[] args) {
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.0.41:26379");
sentinels.add("192.168.0.42:26379");
sentinels.add("192.168.0.43:26379");
JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels);
Jedis jedis = null;
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.println("请输入要保存的key和value。用空格分开:");
String inputLine = sc.nextLine(); // 阻塞
// do something
jedis = pool.getResource();
String[] inputValues = inputLine.split(" ");
jedis.set(inputValues[0], inputValues[1]);
System.out.println(inputValues[0] + " 保存成功...");
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
}