Redis -- 14 -- Redis哨兵模式搭建

相关文章:


官方文档:Redis Sentinel Documentation


之前我们通过不同的方式搭建了 Redis 集群,但是这样的 Redis 集群有个很大的弊端,就是不具备高可用性,因为一旦 Master 挂掉之后,整个 Redis 集群将不能对外提供写入操作,因此 Sentinel (Redis 哨兵) 应运而生

Redis Sentinel 是 Redis 官方提供的集群管理工具,其本身也是一个独立运行的 Redis 进程,它可以监控多个 Master-Slave 集群,当发现 Master 故障之后能进行自动的故障转移,主要有以下几个功能

  • 监控 (Monitoring)

    • Sentinel 会不断地检查主从服务器是否运行正常
  • 通知 (Notification)

    • 当被监控的某个 Redis 服务出现问题时,Sentinel 会通过 API 向管理员或其他应用程序发送故障通知
  • 自动故障转移

    • 当一个 Master 不能正常工作时,Sentinel 会自动进行一次自动故障转移操作,它会将失效 Master 的其中一个 Slave 升级为新的 Master,并让失效 Master 的其他 Slave 去识别新的 Master 进行主从同步

    • 当客户端试图连接失效的 Master 时,集群会向客户端返回新的 Master 地址,使得集群可以使用新的 Master 代替失效的 Master


一、Redis 哨兵分布式性质

  • Redis Sentinel 是一个分布式系统,我们可以在一个架构中运行多个 Sentinel 进程

  • 这些 Sentinel 进程会使用流言协议 (gossip protocols) 来接收 Master 是否下线的信息,并使用投票协议 (agreement protocols) 来决定是否执行自动故障转移,以及选取哪一个 Slave 作为新的 Master

    • 流言协议 (gossip protocols)

      • 每个节点都随机地与对方通信,最终所有节点的状态达成一致

      • 种子节点定期随机地向其他节点发送节点列表以及需要传播的信息

      • 不保证消息一定会传播给所有节点,但是最终会趋于一致


二、Redis 哨兵工作流程

  • 每个 Sentinel 以每秒一次的频率向它所知的 Master、Slave、Sentinel 发送 PING 命令

  • 如果一个实例 (instance) 在指定时间内 (down-after-milliseconds),没有返回 PING 命令的回复,那么该实例会被 Sentinel 标记为主观下线

    • 一个有效的回复可以是:+PONG、-LOADING、-MASTERDOWN
  • 如果一个 Master 被标记为主观下线,那么正在监视这个 Master 的所有 Sentinel 会确认该 Master 是否确实进入了主观下线状态,当有足够数量 (quorum) 的 Sentinel 在指定时间内都同意这一判断,那么该 Master 会被标记为客观下线

    • 主观下线

      • 主观下线 (Subjectively Down,简称 SDOWN),指的是单个 Sentinel 对服务器做出的下线判断
    • 客观下线

      • 客观下线 (Objectively Down,简称 ODOWN),指的是多个 Sentinel 对同一台服务器做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令相互交流之后,做出服务器下线的判断

      • 客观下线条件仅适用于 Master,对于任何其他类型的 Redis 实例 (Slave、Sentinel),Sentinel 在将它们判断为下线前不需要进行协商,Slave、Sentinel 永远不会达到客观下线的条件

      • 当服务器处于客观下线状态,且获得大多数 (majority) Sentinel 的支持,才能发起一次自动故障转移

    • quorum 和 majority 的区别

      • quorum:确认服务器 ODOWN 的最少 Sentinel 数量

      • marjority:授权进行主从切换的最少 Sentinel 数量

      • 举例说明 (假设有 5 个 Sentinel,那么 marjority 则为 3)

        • 1、quorum < marjority,设置 quorum 为 2,如果有 2 (quorum) 个 Sentinel 认为某台服务器 ODOWN,则这 2 (quorum) 个 Sentinel 会选举出一个 Sentinel 来做故障转移,但这个 Sentinel 还需要得到 3 (marjority) 个 Sentinel 的授权,才能真正进行故障转移

        • 2、quorum >= marjority,设置 quorum 为 4,如果有 4 (quorum) 个 Sentinel 认为某台服务器 ODOWN,则这 4 (quorum) 个 Sentinel 会选举出一个 Sentinel 来做故障转移,此外由于 4 (quorum) >= 3 (marjority),所以必须要 4 (quorum) 个 Sentinel 都同意授权,才能真正进行故障转移

  • 当没有足够数量 (quorum) 的 Sentinel 同意 Master 下线,Master 的客观下线状态就会被移除,当 Master 重新向 Sentinel 的 PING 命令返回有效回复时,Master 的主观下线状态就会被移除


三、Redis 哨兵选举策略

  • 如果一个 Master 被 Sentinel 标记为 ODOWN,且 majority 个 Sentinel 都同意授权,那么会有一个 Master 被选举出来,此时需要考虑以下信息

    • 跟 Master 断开连接的时长

    • Slave 的优先级

    • 复制偏移量

    • 运行 ID

  • 如果一个 Slave 跟 Master 断开连接的时长已经超过了 down-after-milliseconds 的 10 倍,外加 Master 宕机时长,那么该 Slave 就被认为不适合选举为 Master,计算公式如下

    • (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
  • 接下来会对 Slave 进行排序

    • 1、按照 Slave 的优先级进行排序,值越小优先级越高 (由 replica-priority 参数进行设置)

    • 2、如果 Slave 的优先级相同,则再检查由 Slave 处理的复制偏移量,偏移量越大优先级越高

    • 3、如果上面两个条件都相同,则再检查运行 ID,运行 ID 越小优先级越高


四、Redis 哨兵配置参数

  • sentinel monitor <master-name> <ip> <redis-port> <quorum>

    • Sentinel 监控的 Master 信息

      参数作用
      master-nameMaster 名称,可自定义
      ipMaster 地址
      redis-portMaster 端口
      quorum法定数量 (确认 Master ODOWN 的最少 Sentinel 数量)
  • sentinel myid <myid>

    • Sentinel 唯一标识
  • sentinel auth-pass <master-name> <password>

    • Sentinel 监控的 Master 密码

    • 注意:sentinel auth-pass 一定要放在 sentinel monitor 后面进行配置,否则 Sentinel 将启动失败

  • sentinel down-after-milliseconds <master-name> <milliseconds>

    • Sentinel 确认 Master SDOWN 的超时时间,默认为 30 s
  • sentinel parallel-syncs <master-name> <numreplicas>

    • 在故障转移期间,允许同时进行转移的 Slave 数量

    • 该值越大,则标识在转移时就有越多的 Slave 不可用,因此需要确保在转移期间,至少能有一个 Slave 可以提供服务

  • sentinel failover-timeout <master-name> <milliseconds>

    • Sentinel 确认故障转移失败的超时时间,默认为 3 min

五、Redis 如何运行 Redis 哨兵

  • 使用 redis-sentinel 命令

    • /path/to/redis-sentinel /path/to/sentinel.conf
  • 使用 redis-server 命令

    • /path/to/redis-server /path/to/sentinel.conf --sentinel

六、搭建 Redis 哨兵

  • 1、在服务器上搭建一个 Master-Slave 集群 (一主二从,Master (6386),Slave (6387 - 6388)),具体流程参考 --> Redis主从同步

  • 2、在 /usr/local/ 目录下新建 redis-sentinel 目录,用于存放哨兵

    • cd /usr/local/

    • mkdir redis-sentinel

  • 3、将之前安装的单机版 Reids (具体流程参考 --> Redis – 02 – Linux上源码包安装Redis) 的 bin 目录下的所有文件 (注意:此处我们需要将根目录下的 sentinel.conf 配置文件复制到 bin 目录下) 复制到 /usr/local/redis-sentinel/sentinel01/ 目录下

    • cp -r /usr/local/redis/bin/ /usr/local/redis-sentinel/sentinel01/
  • 4、修改 /usr/local/redis-sentinel/sentinel01/ 目录下的 sentinel.conf 配置文件

    • vim /usr/local/redis-sentinel/sentinel01/sentinel.conf

      # 关闭保护模式
      protected-mode no
      
      # 设置端口
      port 26379
      
      # 开启守护进程
      daemonize yes
      
      # 设置 pid 文件
      pidfile "/var/run/redis-sentinel-26379.pid"
      
      # 设置 log 文件
      logfile "./sentinel-26379.log"
      
      # 设置工作目录
      dir ./
      
      # 设置监控的 Master 信息
      sentinel monitor mymaster 127.0.0.1 6386 2
      
      # 设置监控的 Master 密码
      sentinel auth-pass mymaster 123456
      
      # 设置 Sentinel 确认 Master SDOWN 的超时时间
      sentinel down-after-milliseconds mymaster 10000
      
      # 设置在故障转移期间,允许同时进行转移的 Slave 数量
      sentinel parallel-syncs mymaster 1
      
      # 设置 Sentinel 确认故障转移失败的超时时间
      sentinel failover-timeout mymaster 180000
      
    • :wq

  • 5、在 /usr/local/redis-sentinel/sentinel01/ 目录下编写启动脚本

    • vim /usr/local/redis-sentinel/sentinel01/start.sh

      #!/bin/bash
      
      #实例路径
      APP_NAME=/usr/local/redis-sentinel/sentinel01/redis-sentinel
      #配置文件路径
      APP_CONF=/usr/local/redis-sentinel/sentinel01/sentinel.conf
      
      #使用说明,用来提示输入参数
      usage() {
      	echo "Usage: run.sh {start|stop|restart|status}"
      	exit 1
      }
      
      #检查程序是否正在运行
      is_running() {
      	pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}'`
      	#如果不存在则返回1,存在则返回0
      	if [ -z "${pid}" ];
      	then
      		return 1
      	else
      		return 0
      	fi
      }
      
      #启动
      start() {
      	is_running
      	if [ $? -eq 0 ];
      	then
      		echo "${APP_NAME} is already running, pid=${pid}"
      	else
      		${APP_NAME} ${APP_CONF}
      	fi
      }
      
      #停止
      stop() {
      	is_running
      	if [ $? -eq 0 ];
      	then
      		kill -9 $pid
      	else
      		echo "${APP_NAME} is not running"
      	fi
      }
      
      #查看运行状态
      status() {
      	is_running
      	if [ $? -eq 0 ];
      	then
      		echo "${APP_NAME} is running, pid is ${pid}"
      	else
      		echo "${APP_NAME} is not running"
      	fi
      }
      
      #重启
      restart() {
      	stop
      	sleep 5
      	start
      }
      
      #根据输入的参数,选择对应的执行方法,不输入则执行使用说明
      case "$1" in
      	start)
      		start
      		;;
      	stop)
      		stop
      		;;
      	status)
      		status
      		;;
      	restart)
      		restart
      		;;
      	*)
      		usage
      		;;
      esac
      
    • :wq

    • chmod +x /usr/local/redis-sentinel/sentinel01/start.sh

  • 6、将 /usr/local/redis-sentinel/sentinel01/ 目录下的所有文件复制 2 份到 /usr/local/redis-sentinel/(sentinel02 - redis03)/ 目录下

    • cp -r /usr/local/redis-sentinel/sentinel01/ /usr/local/redis-sentinel/sentinel02/

    • cp -r /usr/local/redis-sentinel/sentinel01/ /usr/local/redis-sentinel/sentinel03/

    • 在 sentinel02 - sentinel03 两个文件中,修改 sentinel.conf 配置文件和 start.sh 启动脚本

      • sentinel.conf 配置文件

        • port 分别改为 26380 - 26381

        • pidfile 分别改为 /var/run/redis-sentinel-(26380 - 26381).pid

        • logfile 分别改为 ./redis-(26380 - 26381).log

      • start.sh 启动脚本

        • 将 start.sh 启动脚本中的 sentinel01 分别改为 sentinel02 - sentinel03
  • 7、在 /usr/local/redis-sentinel/ 目录下编写总的启动脚本和总的关闭脚本

    • vim /usr/local/redis-sentinel/start.sh

      #!/bin/bash
      
      #项目路径
      SENTINEL01_PATH=/usr/local/redis-sentinel/sentinel01
      SENTINEL02_PATH=/usr/local/redis-sentinel/sentinel02
      SENTINEL03_PATH=/usr/local/redis-sentinel/sentinel03
      
      #使用说明,用来提示输入参数
      usage() {
      	echo "Usage: start.sh {all|sentinel01|sentinel02|sentinel03}"
      	exit 1
      }
      
      #启动所有
      all() {
      	cd ${SENTINEL01_PATH}
      	./start.sh start
      
      	cd ${SENTINEL02_PATH}
      	./start.sh start
      	
      	cd ${SENTINEL03_PATH}
      	./start.sh start
      }
      
      #启动sentinel01
      sentinel01() {
      	cd ${SENTINEL01_PATH}
      	./start.sh start
      }
      
      #启动sentinel02
      sentinel02() {
      	cd ${SENTINEL02_PATH}
      	./start.sh start
      }
      
      #启动sentinel03
      sentinel03() {
      	cd ${SENTINEL03_PATH}
      	./start.sh start
      }
      
      #根据输入的参数,选择对应的执行方法,不输入则执行使用说明
      case "$1" in
      	all)
      		all
      		;;
      	sentinel01)
      		sentinel01
      		;;
      	sentinel02)
      		sentinel02
      		;;
      	sentinel03)
      		sentinel03
      		;;
      	*)
      		usage
      		;;
      esac
      
    • :wq

    • chmod +x /usr/local/redis-sentinel/start.sh

    • vim /usr/local/redis-sentinel/stop.sh

      #!/bin/bash
      
      #项目路径
      SENTINEL01_PATH=/usr/local/redis-sentinel/sentinel01
      SENTINEL02_PATH=/usr/local/redis-sentinel/sentinel02
      SENTINEL03_PATH=/usr/local/redis-sentinel/sentinel03
      
      #使用说明,用来提示输入参数
      usage() {
      	echo "Usage: stop.sh {all|sentinel01|sentinel02|sentinel03}"
      	exit 1
      }
      
      #关闭所有
      all() {
      	cd ${SENTINEL01_PATH}
      	./start.sh stop
      
      	cd ${SENTINEL02_PATH}
      	./start.sh stop
      	
      	cd ${SENTINEL03_PATH}
      	./start.sh stop
      }
      
      #关闭sentinel01
      sentinel01() {
      	cd ${SENTINEL01_PATH}
      	./start.sh stop
      }
      
      #关闭sentinel02
      sentinel02() {
      	cd ${SENTINEL02_PATH}
      	./start.sh stop
      }
      
      #关闭sentinel03
      sentinel03() {
      	cd ${SENTINEL03_PATH}
      	./start.sh stop
      }
      
      #根据输入的参数,选择对应的执行方法,不输入则执行使用说明
      case "$1" in
      	all)
      		all
      		;;
      	sentinel01)
      		sentinel01
      		;;
      	sentinel02)
      		sentinel02
      		;;
      	sentinel03)
      		sentinel03
      		;;
      	*)
      		usage
      		;;
      esac
      
    • :wq

    • chmod +x /usr/local/redis-sentinel/stop.sh

  • 8、启动所有 Redis 实例

    • /usr/local/redis-sentinel/start.sh all
  • 9、查看 Reids 实例进程

    • ps -ef | grep redis

      在这里插入图片描述

  • 10、查看 Sentinel 日志

    • tail -f /usr/local/redis-sentinel/sentinel01/sentinel-26379.log

      在这里插入图片描述

    • tail -f /usr/local/redis-sentinel/sentinel02/sentinel-26380.log

      在这里插入图片描述

    • tail -f /usr/local/redis-sentinel/sentinel03/sentinel-26381.log

      在这里插入图片描述

    • 如上所示,三个哨兵均以开始监控 Master

  • 11、至此,我们就搭建好了一个 Redis 哨兵集群


七、Redis 哨兵测试

  • 关闭 Master,模拟 Master 宕机

    • /usr/local/redis-master-slave/stop.sh redis01
  • 查看 Sentinel 日志

    • tail -f /usr/local/redis-sentinel/sentinel01/sentinel-26379.log

      在这里插入图片描述

    • tail -f /usr/local/redis-sentinel/sentinel02/sentinel-26380.log

      在这里插入图片描述

    • tail -f /usr/local/redis-sentinel/sentinel03/sentinel-26381.log

      在这里插入图片描述

  • 查看节点主从情况

    • /usr/local/redis-master-slave/redis-cli -a 123456 -p 6387

      在这里插入图片描述

    • /usr/local/redis-master-slave/redis-cli -a 123456 -p 6388

      在这里插入图片描述

    • 如上所示,原来端口为 6388 的 Slave,现在已经被升级为了 Master,故障转移成功

  • 开启 Master,模拟 Master 恢复

    • /usr/local/redis-master-slave/start.sh redis01
  • 查看 Redis 日志

    • tail -f /usr/local/redis-master-slave/redis01/redis-6386.log

      在这里插入图片描述

  • 查看节点主从情况

    • /usr/local/redis-master-slave/redis-cli -a 123456 -p 6386

      在这里插入图片描述

    • 如上所示,原来端口为 6386 的 Master,现在已变为了端口为 6388 的 Master 的 Slave


八、参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值