七、redis主从复制和sentinel配置高可用

一:redis主从配置

  • 一、环境准备redis

    master :  192.168.50.10  6179
      slave1:    192.168.50.10  6279
      slave2:    192.168.50.10  6379
  • 二、redis.conf配置文件配置缓存

    • master测试

      port 6179
        requirepass 123456				#密码认证,能够不设置
        dir "/var/redis/6179"                         #工做目录,dump.rdb会保留在这个目录
    • slave1ui

      port 6279
        slaveof 192.168.50.10 6179	 	#master的ip和端口,不能使用127.0.0.1 6179
        masterauth 123456                         #若是master设置了requirepass,则这里须要配置
        slave-read-only yes				#默认就是yes
        dir "/var/redis/6279"
    • slave23d

      port 6379
        slaveof 192.168.50.10 6179	 	#master的ip和端口,不能使用127.0.0.1 6179
        masterauth 123456                         #若是master设置了requirepass,则这里须要配置
        slave-read-only yes				#默认就是yes
        dir "/var/redis/6379"

    注意:若是slaveof 127.0.0.1 6179,则连接sentinel时提示是127.0.0.1,没法链接日志

  • 三、启动redis并测试 ==> 若是设置了daemonize yes则没有日志输出,默认是nocode

    • 启动: nohup redis-server /redis/redis.conf $1>>/var/log/redis/redis.log 2>&1 &server

      master 127.0.0.1:6179>info replicationblog

      slave1 127.0.0.1:6279>info replication进程

      slave2 127.0.0.1:6379>info replication

    • 在master中执行以下语句,若是全部机器执行keys* 都能查出name这个key说明可以复制

      127.0.0.1:6179> set name nametest
  • 四、日志分析

    • slave1和slave2一启动就会去连接master

    • master启动后,接受slave1和slave2的信息同步请求,进行RDB持久化(在内存中持久化)

  • 五、redis主从同步规则

    Salve会发送sync命令到Master
      Master启动一个后台进程,将Redis中的数据快照保存到文件中
      启动后台进程的同时,Master会将保存数据快照期间接收到的写命令缓存起来
      Master完成写文件操做后,将该文件发送给Salve
      Salve将文件保存到磁盘上,而后加载文件到内存恢复数据快照到Salve的Redis上
      当Salve完成数据快照的恢复后,Master将这期间收集的写命令发送给Salve端
      后续Master收集到的写命令都会经过以前创建的链接,增量发送给salve端

    总结一下,主从刚刚链接的时候,进行全量同步;全同步结束后,进行增量同步。固然,若是有须要,slave 在任什么时候候均可以发起全量同步

    注意:在redis2.8开始,slave从新启动,会发送psync到master进行部分同步,而不是sync全量同步

二:Sentinel 组件实现HA配置

  • 一、介绍

    • Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,功能相似于zk,用于监控redis master状态,进行切换。

    • sentinel是和redis独立的组件,不须要每一个redis节点都启动sentinel,能够在任何机器上部署,只要可以链接redis master便可。

    • 其功能相似于zk,用于监听和选举,能够启动多个sentinel做为集群,只要链接的master相同,则认为是统一集群,sentinel集群中各个sentinel也有互相通讯,经过gossip协议

    • 一个sentinel能够监听多个redis集群,他们是经过链接到redis master,利用发布/订阅功能来发现其余sentinel的存在。

  • 二、配置参数介绍

    • 集群1 mymaster

      # 至少2个sentinel认为mymaster挂了,才真正认为是挂了,因此sentinel至少要有2个在运行
        sentinel monitor mymaster 192.168.50.10 6179 2   
        # sentinel向master发送ping,若是在这时间内没有响应,则这个sentinel认为master挂了
        sentinel down-after-milliseconds mymaster 60000
        # 
        sentinel failover-timeout mymaster 180000
        sentinel parallel-syncs mymaster 1
    • 集群2 resque

      sentinel monitor resque 192.168.50.10 6179 4
       	sentinel down-after-milliseconds resque 10000
       	sentinel failover-timeout resque 180000
       	sentinel parallel-syncs resque 5
  • 三、工做原理

    • 连接master,获取slave和其余sentinel信息,记录到本身的sentinel.conf中,以下图

    • sentinel发现down-after-milliseconds 时间内master没有回复,则认为failor,它会问集群其余sentinel是否也有人认为失败,个数达到sentinel monitor mymaster xxx 2,才会触发failover

    • sentinel集群选举其中一个sentinel拿着一个惟一的版本号去进行master切换。此sentinel指定一个slave发送SLAVE OF NO ONE,将其转化为master,同时将这些配置信息广播给其余sentinel,进行更新master信息

  • 四、启动 ===> 若是设置了daemonize yes则没有日志输出

    nohup redis-server redis-slave1/sentinel.conf --sentinel $1>>/var/log/sentinel/sentinel.log 2>&1 &
  • 五、失败测试=> 6379变成了6179的redis

  • 六、注意

    当一个master配置为须要密码才能链接时,客户端和slave在链接时都须要提供密码。
    
      master经过requirepass设置自身的密码,不提供密码没法链接到这个master。
      slave经过masterauth来设置访问master时的密码。
    
      可是当使用了sentinel时,因为一个master可能会变成一个slave,一个slave也可能会变成master,因此须要同时设置上述两个配置项。

三:动态切换master测试

  • 一、pom依赖

    <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.9.0</version>
      </dependency>
  • 二、代码

    public class JedisSentinelTest {
          public static void main(String[] args) {
              /**
               * 获取master连接~方法一
               */
              HashSet<String> sentinelSet = new HashSet<>();
              sentinelSet.add("192.168.50.10:26179"); // 这个节点目前是挂掉的,也能够配置,后续启动有效
              sentinelSet.add("192.168.50.10:26279");
              sentinelSet.add("192.168.50.10:26379");
              JedisSentinelPool myMasterSentinel = new JedisSentinelPool("mymaster", sentinelSet);
    
              Jedis master = myMasterSentinel.getResource();
              master.set("aaaa", "aaaTest");
              master.close();
    
              /**
               * 获取master连接~方法二
               */
              Jedis sentinelNode = new Jedis("192.168.50.10", 26179);
              List<Map<String, String>> masterList = sentinelNode.sentinelMasters();
              Map<String, String> masterMap = null;
              for (Map<String, String> temp : masterList){
                  if ("mymaster".equals(temp.get("name"))){
                      masterMap = temp;
                      break;
                  }
              }
              System.out.println("masterIp:" + masterMap.get("ip"));
              System.out.println("masterPort:" + masterMap.get("port"));
    
              List<String> mymaster = sentinelNode.sentinelGetMasterAddrByName("mymaster");
              System.out.println("masterIp:" + mymaster.get(0));
              System.out.println("masterPort:" + mymaster.get(1));
    
              /**
               * 获取slaves
               */
              Jedis jedis = new Jedis("192.168.50.10", 26179);
              List<Map<String, String>> slaves = jedis.sentinelSlaves("mymaster");
              for (Map<String, String> temp : slaves){
                  System.out.println("slaveIp:" + temp.get("ip"));
                  System.out.println("slaveIp:" + temp.get("port"));
              }
    
              /**
               * 主从切换
               */
              Map<String, String> first = slaves.get(0);
              Jedis toBeMaster = new Jedis(first.get("ip"), Integer.parseInt(first.get("port")));
              String flag = toBeMaster.slaveofNoOne();  // 将本身设置为主节点
    
              for (int i = 1; i < slaves.size(); i++){
                  Map<String, String> temp = slaves.get(i);
                  Jedis ortherSlave = new Jedis(temp.get("ip"), Integer.parseInt(temp.get("port")));
                  ortherSlave.slaveof(first.get("ip"), Integer.parseInt(first.get("port")));
              }
    
              Jedis slave = new Jedis("192.168.50.10", 6279);
              /**
               * 从新加载配置信息,复原
               */
              slave.configResetStat();
    
              /**
               * 手动触发RDB(save、bgsave)以及aof rewrite
               */
              slave.save();
              slave.bgsave();
              slave.bgrewriteaof();
    
              /**
               * 获取信息,能够知道本身是什么角色
               */
              String info = slave.info();
              String replication = slave.info("replication");
          }
      }
相关文章
相关标签/搜索