概念
Sentinel只是一个运行在特殊模式下的Redis服务器,它使用了和普通模式不同的命令表,所以Sentinel模式能够使用的命令和普通Redis服务器能够使用的命令不同。用于redis主从架构环境中。
原理
-
三个定时任务
- 每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构
- Sentinel向Redis数据节点的sentinel:hello 频道发送信息,所有sentinel节点都订阅该频道,用于交换各个sentinel节点的状态,以及发现新节点。
- 每个sentinel向其它各个节点发送心跳检测
-
主观下线
当Sentinel将一个主服务器判断为主观下线时,它会向同样监视这个主服务器的其他Sentinel进行询问,看它们是否同意这个主服务器已经进入主观下线状态。
-
客观下线
当Sentinel收集到足够多的主观下线投票之后,它会将主服务器判断为客观下线,并发起一次针对主服务器的故障转移操作。
主观下线并不一定下线,有可能是这台sentinel和主服务器之间存在通信故障,需要选举投票。
- Sentinel领导者选举
- 所有在线的Sentinel都有被选为领头Sentinel的资格,换句话说,监视同一个主服务器的多个在线Sentinel中的任意一个都有可能成为领头Sentinel。
- 每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元(configuration epoch)的值都会自增一次。配置纪元实际上就是一个计数器,并没有什么特别的。
- 在一个配置纪元里面,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改。
- 每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel。
- 当一个Sentinel(源Sentinel)向另一个Sentinel(目标Sentinel)发送SENTINEL is-master-down-by-addr命令,并且命令中的runid参数不是*符号而是源Sentinel的运行ID时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局部领头Sentinel。
- Sentinel设置局部领头Sentinel的规则是先到先得:最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,而之后接收到的所有设置要求都会被目标Sentinel拒绝。
- 目标Sentinel在接收到SENTINEL is-master-down-by-addr命令之后,将向源Sentinel返回一条命令回复,回复中的leader_runid参数和leader_epoch参数分别记录了目标Sentinel的局部领头Sentinel的运行ID和配置纪元。
- 源Sentinel在接收到目标Sentinel返回的命令回复之后,会检查回复中leader_epoch参数的值和自己的配置纪元是否相同,如果相同的话,
那么源Sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel。- 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel,那么这个Sentinel成为领头Sentinel。举个例子,在一个由10个Sentinel组成的Sentinel系统里面,只要有大于等于10/2+1=6个Sentinel将某个Sentinel设置为局部领头Sentinel,那么被设置的那个Sentinel就会成为领头Sentinel。
- 因为领头Sentinel的产生需要半数以上Sentinel的支持,并且每个Sentinel在每个配置纪元里面只能设置一次局部领头Sentinel,所以在一个配置纪元里面,只会出现一个领头Sentinel。
- 如果在给定时限内,没有一个Sentinel被选举为领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止。
- 故障转移。
- 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
- 让已下线主服务器属下的所有从服务器改为复制新的主服务器。
- 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。
遇到问题汇总
- 至少三个Sentinel在三个不同的盒子里
- 单数个sentinel
- 三个sentinel + 1 个master +2 个slave 最低测试环境
其它问题
- Sentinel在客户端盒子里,什么实现?
java 访问方式
- 直接使用Jedis访问:
//连接池配置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(100);
jedisPoolConfig.setMaxIdle(20);
jedisPoolConfig.setMinIdle(5);
//哨兵配置列表
Set<String> sentinelList = new HashSet<String>(Arrays.asList(
"127.0.0.1:16379",
"127.0.0.1:26379",
"127.0.0.1:36379"
));
JedisSentinelPool pool = new JedisSentinelPool("sentinelsMasterName", sentinelList, jedisPoolConfig, "password");
//获取客户端
Jedis jedis = pool.getResource();
jedis.set("demo", "100");
String demoValue = jedis.get("demo");
- 使用 template 实现:
RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
String[] host = new String[]{
"127.0.0.1:16379",
"127.0.0.1:26379",
"127.0.0.1:36379"
};
for(String redisHost : host){
String[] item = redisHost.split(":");
String ip = item[0];
String port = item[1];
configuration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
}
configuration.setMaster("RedisMasterName");
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(100);
jedisPoolConfig.setMaxIdle(20);
jedisPoolConfig.setMinIdle(5);
JedisConnectionFactory jedisConnectionFactory =
new JedisConnectionFactory(configuration, jedisPoolConfig);
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//设置开启事务
template.setEnableTransactionSupport(true);
//set key serializer
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setConnectionFactory(jedisConnectionFactory());
template.afterPropertiesSet();
网友评论