Redis_Sentinel

Redis集群的主从复制

  在redis主从复制中,如果某个节点挂掉了,需要我们去写手动写脚本,来时转移节点。

 

  Redis Sentinel的功能:对Redis节点进行监控,故障判断,故障转移,故障通知

  同时多个sentinel运行,即使一个sentinel进程运行异常,还有别的sentinel继续运行,可以保证对故障节点判断的准确性,同时保证Redis的高可用

  对于redis-cli来说,Redis cli不会再记录Redis的IP和端口,而是从sentinel获取Redis信息,然后进行连接Redis节点,进行数据写入和读取操作

  多个Redis Sentinel对所有的master和slave进行监控,会实时记录master和slave的地址信息

 

  Redis Sentinel就是一种高可用的架构:

      

 

  有很多sentinel节点,它不用存储数据,主要监控redis节点是否发生故障并且通知客户端。

  redis Sentinel如何做到自动高可用故障转移:

     

  具体流程

1.当某个master发生故障,多个sentinel会监控到这个异常,这些sentinel会按照一定规则从多个slave中选中一个做为新的master,并通知别的slave从新的master中同步数据
2.当某个slave转换为新的master,sentinel会记录新的master的地址信息和slave的地址信息,通知Redis cli
3.Redis cli接收到新的master和slave的信息,就会向新的master写入数据,从slave中读取数据
4.等到原来的master重启之后,会变成新的master的slave,并从新的master同步数据

  同样一个sentinel可以对多组master-slave做监控。

  当master节点宕掉的时候:

  1.sentinel节点会从slave节点中选择一个合适的节点作为新的master节点。

  2.对上面选出的slave节点执行slaveof no one命令让其成为master节点。

  3.向剩余的slave节点发送命令,让他们成为新master节点的slave节点,复制规则和parallel-syncs参数有关。

  4.更新对原来master节点配置为slave,并保持对其“关注”,当其恢复后,命令它去复制新的master节点。

  Redis Sentinel实现原理:Redis Sentinel内部有三个定时任务来对redid节点进行故障判断和转移

  1.每10秒每个sentinel对master和slave执行info命令,以发现slave节点和确认主从关系,sentinel在master节点执行info replication命令,从命令执行结果中解析出slave节点

     

  2.每2秒每个sentinel通过master节点的channel交换信息(发布订阅),master节点上有一个发布订阅的channel频道:__sentinel__:hello,用于所有sentinel之间进行信息交换

    一个sentinel发布消息,消息包含当前sentinel节点的信息,对其他sentinel节点的判断以及当前sentinel对master节点和slave节点的一些判断,其他sentinel都可以接收到这条消息,新加入sentinel节点时,sentinel节点之间可以相互感知,以达到信息交互的功能

   

  3.每1秒每个sentinel对其他sentinel节点和Redis节点执行ping操作,每个sentinel都可以知道其他sentinel节点,当监控的master发生故障时,方便进行判断和新master的挑选,这个定时任务是master进行故障判定的依据

       

  主观下线和客观下线:

  主观下线:每个sentinel节点对Redis节点失败的'偏见'

  Sentinel的配置中有一个down-after-milliseconds的时间:

  每个sentinel每秒对master和slave执行ping操作,当sentinel对master或slave在timeout定义的毫秒时间内没有回复,则sentinel会认为这个节点已经被主观下线了

  客观下线

  sentinel集合监控名为mymaster的master,slave节点 被监控的master节点的IP地址是192.168.81.100,端口为6379, sentinel会在`__sentinel__:hello`频道中交流对master节点的看法,如果sentinel节点都对master节点ping失败'达成共识',sentinel个数超过quorum(一般是sentinel节点个数一半)的个数,sentinel集合则会认为master节点客观下线

  sentinel领导者选举具体过程:

1.每个做主观下线的sentinel节点向其他sentinel节点发送命令,要求将自己设置为领导者
2.收到命令的sentinel节点如果没有同意同意其他sentinel节点发送的命令,那么将同意该请求,否则拒绝
3.如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,将成为领导者
4.如果此过程中有多个sentinel节点成为领导者,那么将等待一段时间重新进行选举

  故障的转移由sentinel领导者节点来完成;

  选取slave节点作为master节点的具体过程:

1.选择slave-priority(slave节点优先级)最高的slave节点,如果存在则返回,不存在则继续
2.选择复制偏移量(offset)最大的slave节点,offset最大说明对master的数据复制的最完整,如果存在则返回,不存在则继续
3.选择run_id最小的slave节点,run_id最小说明slave节点启动最早

  jedis客户端使用redis Sentinel

JedisSentinel sentinelPool = new JedisSentinelPool(masterName,sentinelSet,poolConfig,timeout);
Jedis jedis = null;
try{
   jedis = redisSentinelPool.getResource();
}catch(Exception e){
}finally{
   if(jedis!=null)
     jedis.close();
}
public class RedisSentinelClient {
    private static JedisSentinelPool pool = null;
    private static String redisHosts = "127.0.0.1:26378;127.0.0.1:26379;127.0.0.1:26380";
    private static String redisMaster = "";//master name
    private static String password = "";//密码,可选
    private static final int MAX_IDLE = 200;//最大空闲数
    private static final int MAX_TOTAL = 400;//最大连接数
    private static final int MIN_IDLE = 200;//最小空闲数
 
    static {
        //redis 连接池配置
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxIdle(MAX_IDLE);
        poolConfig.setMaxTotal(MAX_TOTAL);
        poolConfig.setMinIdle(MIN_IDLE);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        Set<String> hosts = new HashSet<String>(Arrays.asList(redisHosts.split(";")));
        if (StringUtils.isBlank(password)) {
            pool = new JedisSentinelPool(redisMaster, hosts, poolConfig);
        } else {
            pool = new JedisSentinelPool(redisMaster, hosts, poolConfig, password);
        }
    }
 
    public String get(String key) throws JedisConnectionException {
        Jedis jedis = pool.getResource();
        try {
            return jedis.get(key);
        } catch (JedisConnectionException e) {
            throw e;
        } finally {
            jedis.close();
        }
    }
}

posted @ 2019-04-27 23:17  LeeJuly  阅读(192)  评论(0)    收藏  举报