redis-哨兵

1、基本概念和架构

1.1 基本概念

哨兵,Redis sentinel,在主从复制的基础上实现故障恢复的自动化。其核心功能是主节点(master)的自动故障转移html

主要功能:java

  1. 监控(Monitor):哨兵不断检查主节点和从节点是否正常工做。
  2. 自动故障转移(Automatic failover):主节点不正常时,哨兵启动自动故障转移,它会将失效主节点的其中一个从节点升级为新的主节点,并让其余从节点从这个新的主节点复制数据。
  3. 配置提供者(Configuration provider):客户端能够经过哨兵来获取主节点地址。
  4. 通知(Notification):哨兵能够把故障转移结果通知给客户端。

1.2 架构

它由两部分组成,哨兵节点和数据节点:redis

  1. 哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的redis节点,不存储数据
  2. 数据节点:主节点和从节点都是数据节点。

redis sentinel是一个分布式的架构,其中包含若干个sentinel节点,对于节点的故障判断是由多个独立的sentinel节点共同完成,这样能够有效防止误判。多个sentinel节点使得即便个别sentinel节点不可用,整个sentinel节点集合依然是健壮的。算法

2、部署演示

总体部署在一台机器上(192.168.118.129 ),经过不一样的端口号来区分主从节点以及哨兵节点。架构

2.1 部署主从节点

部署一台主节点(端口号为6379),2台从节点(端口号分别为6380和6381)。分布式

把6380配置为6379的slave。ide

把6381配置为6379的slave。函数

查看master节点的复制信息线程

2.2 部署哨兵节点

配置三个哨兵节点,和主从在同一台机器上(192.168.118.129),用三个不一样的端口号来区分(分别为26379,26380,26381)。debug

哨兵节点是特殊的redis节点,有特殊的配置,针对每个哨兵分别创建一个配置文件。

以26379哨兵为例,创建sentinel26379.conf配置文件,基本配置以下:

sentinel monitor mymaster 193.168.129 6379 2

这句是哨兵节点的核心配置,表明这个哨兵监控193.168.129: 6379这个master节点,且命名为mymaster。2表明至少有两个哨兵赞成才能认定主节点产生故障而且进行故障转移。

按照上述步骤配置sentinel26380.conf和sentinel26381.conf。至此,三个哨兵节点配置完成。

接下来启动哨兵节点。有两种方法,两个方法等效

  1. src/redis-sentinel sentinel26379.conf
  2. src/redis-server sentinel26379.conf --sentinel

一次启动三个哨兵,启动后能够查看。

启动后哨兵节点会发现主节点,以及主节点对应的salve节点,同时哨兵节点会发现彼此。哨兵节点启动后最终的结构以下。

2.3 故障转移演示

演示主节点down机后哨兵进行自动故障转移的case。目前主节点是6379节点。

杀掉主节点的进程

迁移

主节点已经被迁移成了6380节点。

转移后配置文件也被自动作了修改。

3、客户端链接(配置提供者)

哨兵做为配置的提供者,客户端能够直接基于哨兵进行链接,而无需直到具体主节点的ip,这样实现了和主节点的解耦,当主节点down机后,哨兵从新选主,对客户端透明。

3.1 代码

public static void main(String[] args)
    {
        String masterName="mymaster";// 主节点名字
        Set<String>  sentinels=new HashSet<>();//哨兵集合
        sentinels.add("192.168.118.129:26379");
        sentinels.add("192.168.118.129:26380");
        sentinels.add("192.168.118.129:26381");
        JedisSentinelPool jedisSentinelPool=new JedisSentinelPool(masterName,sentinels);
        Jedis jedis= jedisSentinelPool.getResource();
        jedis.set("name","cnblogs-new");
        System.out.println(jedis.get("name"));
        jedisSentinelPool.close();
    }

执行结果:

[main] INFO redis.clients.jedis.JedisSentinelPool - Trying to find master from available Sentinels...
[main] INFO redis.clients.jedis.JedisSentinelPool - Redis master running at 192.168.118.129:6380, starting Sentinel listeners...
[main] INFO redis.clients.jedis.JedisSentinelPool - Created JedisPool to master at 192.168.118.129:6380
cnblogs-new

能够看到经过哨兵找到了master节点。

3.2 原理

核心实如今JedisSentinelPool的构造函数中

private HostAndPort initSentinels(Set<String> sentinels, final String masterName) {
    HostAndPort master = null;
    log.info("Trying to find master from available Sentinels...");
    for (String sentinel : sentinels) {
        final HostAndPort hap = HostAndPort.parseString(sentinel);
        log.debug("Connecting to Sentinel {}", hap);
        jedis = new Jedis(hap.getHost(), hap.getPort(), sentinelConnectionTimeout, sentinelSoTimeout);
        List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName); //查找master
        master = toHostAndPort(masterAddr);
        return master;
     }
 }

经过代码能够发现:

  1. 遍历哨兵节点

  2. 链接哨兵节点

  3. 经过哨兵节点获取master节点 sentinelGetMasterAddrByName。改方法内部调用了redis的命令来实现 sentinel get-master-addr-by-name mymaster

上述代码省略了一部分,订阅。

for (String sentinel : sentinels) {
      final HostAndPort hap = HostAndPort.parseString(sentinel);
      MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
      // whether MasterListener threads are alive or not, process can be stopped
      masterListener.setDaemon(true);
      masterListeners.add(masterListener);
      masterListener.start();
    }

为每个sentinel节点独立启动一个线程,利用redis的发布订阅功能,每一个线程订阅sentinel节点上切换master的相关频道+switch-master

public void onMessage(String channel, String message) {
   String[] switchMasterMsg = message.split(" ");
   if (masterName.equals(switchMasterMsg[0])) {
      initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4])));
   } else {
      log.debug("Ignoring message on +switch-master for master name {}, our master name is {}",switchMasterMsg[0], masterName);
        }
}

发现当前master发生了switch,使用initPool从新初始化链接池。

4、实现原理

4.1 三个定时任务

经过监控来判断节点是否不可达,sentinel经过三个定时任务来完成后对各个节点的监控和发现。

Sentinel节点定时执行info命令来获取拓扑信息

每隔10秒执行

  1. 经过向master 执行info能够拿到从节点的信息(这就解释了为何sentinel没有显示配置从节点,可是能够监发现从节点)。
  2. 从节点加入,能够自动感知。
  3. 节点不可达或者故障转移后,能够经过info实时拿到拓扑信息。

Sentinel节点发布和订阅 _sentinel_hello频道

每隔两秒执行

每隔2秒,每隔sentinel节点会向master的_sentinel_hello 频道发送自身的节点信息,以及本身对master状态的判断,同时每一个sentinel也会订阅改频道,最 终实现了sentinel节点之间的互相发现以及彼此交换本身对主节点状态的判断,做为客观下线的依据。

Sentinel节点向其他节点发送ping命令

每隔1秒执行

实现对每一个节点的监控,这个定时任务是节点失败断定的重要依据。

4.2 主观下线和客观下线

一个参数: down-after-milliseconds

# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached replica or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
#
# Default is 30 seconds.

主观下线

上一节里面的ping操做,若是超过 down-after-milliseconds没有获得回复,sentinel会把改节点作失败判断,叫作主观下线。就是当前sentinel一家认为这个节点是失败的,有可能有误判。

客观下线

当sentinel对master作了主观下线后会经过命令: sentinel is-master-down-by-addr来询问其余sentinel 节点对主节点是否下线的判断。当超过 数量的sentinel认为主节点已经下线的时候,就能够把master节点表示为客观下线(完全下线)。

总结举例

master至关于一个嫌疑人,在审判,法律规定必需要3(quorum)个陪审员都认为有罪,才会判断为有罪。当任何一个陪审员认为嫌疑人有罪的时候,就问其余陪审员的意见,若是最终汇总下来发现有3个陪审员认为有罪,则最终才会判断master有罪,经过多个确认来减小误判的可能。

客观下线是针对master节点的,slave节点只须要主观下线便可。

4.3 领导者Sentinel节点选举

当最终对主节点进行客观下线后,并非立刻进行故障转移,而是进入领导者sentinel节点的选举,由于最终的故障转移由一个sentinel节点来完成,这个节点就是领导者sentinel节点。

领导者选取使用的算法是raft算法,(参考2),是一个共识算法,大体思路以下。

  1. 每一个sentinel节点都有资格成为领导者,当它确认主节点主观下线后,向其余sentinel节点发送 is-master-down-by-addr,要求将本身设置为领导者,固然给本身投了一票
  2. 其余sentinel收到命令后,若是没有赞成过其余sentinel is-master-down-by-addr命令,则赞成该请求(只投一次赞成票)。
  3. 若是sentinel节点发现本身的票数已经大于或者等于max(quorum, num(sentinels)/2+1),那么它将会成为领导者sentinel节点
  4. 若是本次没有选举出领导者,将进入下一次选举。

S1最早完成了客观下线。

  1. 向S2请求,S2第一次收到,恢复YES
  2. 向S3请求,S3第一次收到,恢复YES

S1获得两票,知足条件,成为leader。

节点 发出赞成的节点 接受赞成的节点
S1 S2,S3
S2 S1
S3 S1

4.4 故障转移

从新选取master

当领导者sentinel选举完成后,由该sentinel节点完成故障转移。总体流程以下。

  1. 过滤不健康节点是指主观下线,断线的salve

  2. 先比较replica_priority, 值越小,优先级越高

  3. 若是priority相同,则选择复制偏移量最大的节点(最接近原来master的节点,数据最全)

  4. 若是偏移量相同,则选择runid最小的节点

设置过程

  1. 领导者sentinel经过执行replicaof no one命令让选举出来的最新节点成为最新的master
  2. 领导者sentinel向其余从节点发送 replicaof 命令,让其成为最新master的从节点
  3. 同时将已经下线的原主节点设置为新master的从节点,等其上线后自动成为新的主节点的从节点。
相关文章
相关标签/搜索