相关文章:
之前我们通过不同的方式搭建了 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-name Master 名称,可自定义 ip Master 地址 redis-port Master 端口 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
- 将 start.sh 启动脚本中的
-
-
-
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
-