0. 引言
上一章我们讲解了redis的主从搭建,但要实现真正的可靠的主从结构,还需要实现主从切换。也就是当主节点宕机时,从节点能够自动切换为主节点。
这就需要借助哨兵模式来实现,今天我们就来看如何搭建哨兵模式
1. 原理
1.1 哨兵的作用
所谓哨兵就是有一个放哨的,一旦发现主节点不行了,就会告诉从节点顶上。
它的核心作用有三个:
- 监控节点状态,判断主节点是否挂掉
- 当主节点挂掉时,进行主从切换,选出新的主节点
- 将选出新主的消息告诉其他节点以及客户端
所以它的原理其实就围绕这三点展开
1.2 监控节点状态
哨兵会定时发送三个任务:
(1)每10s会向主节点发送info
指令,并通过主节点来获取配置的从节点信息,也就是主从节点的网络拓扑图(结构信息)。这样当有新的从节点配置进来时可以感知到
(2)每2s广播当前哨兵对于主节点的判断(是否主观下线)以及当前哨兵节点的信息(让别的哨兵知道“我”还活着)。这里广播是通过redis的发布订阅模式来实现的。
(3)每1s会向主从节点、其他哨兵发送ping命令,也就是心跳检测,检查这些节点是否还活着。
1、 从第三个任务我们可以看到,哨兵每1s会发送一个ping
命令给监控的主从节点,在规定时间内(is-master-down-after-milliseconds
)没有收到有效回复时就会认为这个节点下线
了;
但是这里会涉及一个问题,生活中我们一般认为一个人的想法是主观想法,大多数人的想法近似于客观想法。redis中也有这样一个概念,一个哨兵节点认为某个节点挂了,这是主观下线;大多数哨兵节点认为某个节点挂了,这是客观下线。这样的好处也很明显,可以减少误判。
比如说某个哨兵节点的网络刚好有波动,ping节点时没有ping通,结果就认为redis节点挂了,实际上是网络的问题。而大多数哨兵都同时出现网络问题的概念就比较低了,触发机房整体出现网络问题,那这是redis节点也自然挂了。
2、 所以当一个哨兵节点认为这个主节点主观下线后,就会发送命令is-master-down-by-addr
询问其他哨兵,该节点有没有下线;
3、 如果达到指定数量(quorum
,默认半数以上)的哨兵节点都认为某个主节点主观下线了,该哨兵就会认为该主节点客观下线
了;
通过这样来监控节点的状态
1.3 哨兵的的个数
当哨兵判定某个节点为客观下线
后,就会向其他哨兵发起投票,选出一个leader哨兵,最后由这个leader哨兵来进行主从切换
所以我们一般要求哨兵节点为2个及以上,且为奇数,也就是至少3个。想想为什么?
因为如果为偶数个的哨兵节点时,就会出现"脑裂"问题,这个在es集群中也比较常见,比如一半的哨兵投了反对票, 一半的投了支持票,请问最终结果如何?
其次因为哨兵需要半数以上投票通过才能通过,当为2节点时。如果宕机了一个节点,就永远只有一个节点,则永远不能超过半数以上了,而为2节点时,即使宕机1个,还有2个节点可以投票,也能满足半数以上。
所以,我们必然需要奇数个哨兵才能真正投票出结果
1.4 哨兵的投票机制
但奇数个哨兵就万事大吉了吗?如果每个哨兵都投个自己,那不是没法选出一个leader了吗?
redis哨兵的投票算法当然想到了这点。
1、 首先,最先发现主节点为客观下线的哨兵,redis将其定义为“候选者”;
2、 候选者会先投自己一票,可以理解为“我发现的问题,我要自己解决”;
3、 然后会向其他哨兵发起投票请求(请把票投给我),每个哨兵都有一票,候选者可以把票投给自己,非候选者收到投票请求后,可以选择同意或拒绝,如果收到两个候选者的投票请求,先到先得;
4、 候选者会统计自己获取到的票数,直到获取的票数超过半数,且超过所配置的quorum
数,这次选举就成功了,会晋升为Leader,并且负责主从切换事宜;
1.5 哨兵主从切换
当选举出哨兵Leader后,就可以进行主从切换了。
1、 从挂掉的主节点下的所有的从节点中选一个状态健康、优先级高且数据完整的从节点;
所谓状态健康就是网络连接正常的,主要是主从节点在
down-after-milliseconds
时间内产生过网络通信的即可认为是健康的从节点。而优先级高则是根据
slave-priority
配置的值来判定的数据完整则是当优先级相同,复制的数据下标越多则数据越完整(主从复制时从节点会记录复制的数据下标),如果优先级和数据完整都想同时就会选择节点ID最小的那个
这里大家可能会想:“哨兵是怎么知道主节点下有哪些从节点的,主节点不是都挂掉了吗?”
答:还记得我们上面讲的哨兵的三个定时任务吗?哨兵就是通过第一个定时任务获取的从节点信息,并且记录在册,以供使用。
2、 然后通过指令slaveofnoone
清除这个从节点的SLAVE信息,也就是取消掉其附属的主节点,让其成为主节点;
3、 同时将挂掉的主节点的所有从节点信息再挂载到新的主节点上;
4、 至此,内部的主从转换就完成了,但下一步我们还需要将新主节点的信息广播给客户端当然这里的广播是通过redis的发布/订阅模式来实现的;
5、 还没完,旧的主节点哨兵也会持续监听,当它恢复后,哨兵会将其作为从节点挂载到新主节点上所以这里要注意,主节点宕机后再恢复,并不会自动再切回主节点,而是作为从节点存在;
至此,我们关于哨兵模式的原理就介绍完成了。下面我们来看具体实操。
2. 哨兵搭建
1、 首先准备三个节点,一主双从,并启动如果不知道如何搭建的,可以参考我之前的文章:;
2、 可以单独启动redis节点作为哨兵节点,也可以配置到原有的数据节点(主从节点)中,这里为了节约服务器资源,我们将哨兵配置到原来的主从节点中;
3、 哨兵有一个单独的配置文件sentinel.conf
,在redis的安装目录下可以看到;
4、 修改三个节点的配置文件;
vim sentinel.conf
修改内容:
# 关闭保护模式
protected-mode no
# 哨兵监听端口,默认26379
port 26379
# 后台启动,即开启守护进程
daemonize yes
# 日志路径
logfile "/var/local/redis/logs/sentinel.log"
# 指定数据存放路径,默认/tmp
dir "/tmp"
# 监听的主节点 mymaster为主节点名称,最后的2为quorum值
sentinel monitor mymaster 192.168.244.27 6379 2
# 如果master节点有密钥要补充密码
#sentinel auth-pass mymaster xxx
# 判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel down-after-milliseconds mymaster 30000
# 故障节点的最大超时时间为180000(180秒)
sentinel failover-timeout mymaster 180000
5、 如果未关闭防火墙,需要开启哨兵监听端口;
# 查询端口是否开放
firewall-cmd --query-port=26379/tcp
# 开启端口
firewall-cmd --add-port=26379/tcp --permanent
# 开启后重新加载
firewall-cmd --reload
6、 启动哨兵;
cd redis安装目录
redis-sentinel sentinel.conf
7、 查看日志;
tail -f /var/local/redis/logs/sentinel.log
8、 查看哨兵是否启动;
(1)通过ps指令查询
ps -ef | grep sentinel
(2)通过redis-cli连接哨兵节点,连接成功说明启动成功
3. 测试
1、 关闭主节点;
(1)查询进程
ps -ef | grep reids
(2)kill 主节点
kill -9 1212
2、 查看哨兵日志;
可以看到已经切换到从节点28服务器了
3、 查看从节点信息,到新主节点上执行;
redis-cli info replication
从节点已经更新为主节点了,并且其下现在只有一个从节点
4、 我们再把原主节点启动起来;
service redis start
# 我这里已经将启动脚本配置到/etc/init.d文件夹并命名为redis了,所以可以通过service启动,如果没有配置的,使用以下指令启动
redis-server /etc/redis/6379.conf
5、 观察哨兵日志,发现原主节点被转换为从节点;
6、 再次观察从节点信息;
新增了一个从节点,即原来的主节点,与我们上述的原理解析相符
7、 我们在尝试在新节点上更新一个key;
8、 从节点查看该key;
主从切换成功,哨兵搭建完成
4. 配置哨兵开机启动
1、 之前我们已经讲解了设置redis开机自启,我们在此基础上操作;
2、 修改原有redis启动脚本;
vim /etc/init.d/redis
脚本内容
REDISPORT=6379
# 配置哨兵端口
REDIS_SENTINEL=26379
EXEC=/var/local/redis/redis-6.2.7/src/redis-server
CLIEXEC=/var/local/redis/redis-6.2.7/src/redis-cli
# 配置哨兵执行脚本
EXEC_SENTINEL=/var/local/redis/redis-6.2.7/src/redis-sentinel
PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"
# 哨兵配置文件路径
SENTINEL_CONF="/var/local/redis/redis-6.2.7/sentinel.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
# 启动哨兵服务
echo "Starting Redis-Sentinel server..."
$EXEC_SENTINEL $SENTINEL_CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
# 关闭哨兵服务
echo "Stoping sentinel..."
$CLIEXEC -p $REDIS_SENTINEL shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
3、 如果之前没配置过,则需要操作以下步骤,如果已经按照过我之前的配置添加过redis开机自启的则无需配置;
(1)赋予文本权限
chmod 777 /etc/init.d/redis
(2)添加开机自启
chkconfig redis on
4、 重启哨兵服务器,连接哨兵端口测试;
redis-cli -p 26379
连接成功,说明启动成功
总结
至此,我们哨兵模式的搭建就完成了,希望大家可以借此更加深入理解redis原理,下一期,我们继续讲解redis的集群模式,实现真正的redis高可用