故障转移的基本流程
哨兵发现主库下线并选出新主库的流程称为故障转移,这个过程需要解决三个问题:
主库真的挂了吗?(监控)
该选择那个从库作为主库?(选主)
怎么把新主库的相关信息通知给从库和客户端呢?(通知)
监控
主观下线”。
客观下线”;否则,会重新将 master 标记为上线状态。
选主
哨兵 Leader 选举
哨兵 Leader 是本次故障转移的执行者,每个哨兵都有机会成为 Leader。具体流程如下:
当哨兵将 master 节点标记为客观下线后,会将当前纪元(epoch,该值类似 raft 协议的 term 值)加一,表示开始一次新的 leader 选举。
首先会给自己投一票,然后会再次发送 给其它哨兵。这个命令的使用方式如下:。这里主要看 runid,确认主节点是否客观下线时,该值为 *;开始参选 Leader 时,传的参数为本节点的 runid。
收到命令的哨兵,如果没有同意过其他节点的命令,将同意该请求,否则拒绝。并且同意过其它节点后,该节点也不会发起投票。
如果该哨兵发现自己的票数已经大于等于,那么它将成为领导者。
如果此过程没有选举出领导者,将进入下一次选举。选举超时时间可以查看配置 。
筛选
首先会判断从库当前的状态,过滤掉当前主观下线以及断线的节点;
5 秒内没有回复过哨兵节点 PING 请求的节点;
与主节点断连次数超过 10 次的节点。是否断连是根据配置项 来判断的,主从节点 毫秒内都没有连接上,则会被记为一次断连。
打分
-
通过配置项 slave-priority 来给不同的从库设置不同的优先级
。可以使用命令 来查看该配置。如果有一个从库的优先级最高,那么该从库会直接选为新主库;否则,将会进行第二轮打分。
与旧主库同步程度最接近的从库得分高。在主从同步中提到过,从库会用 来记录当前从 master 的复制进度,该配置通过 可以查看。哨兵会选出复制进度值最大的从库作为新的主库,如果多个从库复制进度并列第一,那么需要进行第三轮打分。
ID 号最小的从库得分高。最后,哨兵会查看从库的 runId,将 runId 最小的从库作为新主库。runId 通过命令 可以查看。
通知
-
告诉新主库它已经称为了新主库
。哨兵会向目标库发送命令 ,收到命令后,目标库会将自己的角色( role 提升为。
告诉其它从库新主库的地址。哨兵会向其它从库发送 来告诉从库新主库的地址,从库会执行 命令开始从新主库同步。
通知客户端主库变化。当新主库切换完成后,哨兵会在频道 中发送消息,通知外部客户端新主库地址。
哨兵集群
上面讨论主从切换的时候有提到哨兵集群来减少主库客观下线误判的可能性。哨兵监控一个 master 节点是通过下面这个命令来完成的:
sentinel monitor <master-name> <ip> <redis-port> <quorum>
这个命令中并没有指定其它哨兵的地址信息,那么哨兵是如何组成一个集群的呢?
基于 pub/sub 机制的哨兵集群
sentinel:hello” 的频道,然后会将自己的地址端口等信息发布到该频道,其它哨兵就可以从这个频道获取监控同一个主节点的哨兵信息,互相建立网络连接,形成一个集群。
获取从库信息
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.26.0.3,port=6379,state=online,offset=217874,lag=0
slave1:ip=172.26.0.2,port=6379,state=online,offset=217874,lag=0
master_failover_state:no-failover
master_replid:51567b12a9c1ba1d82846a8fb8fd84404b60eaf8
master_replid2:0baa276d98298739b2e0755640fd5b50d2828b26
master_repl_offset:217874
second_repl_offset:17993
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:429
repl_backlog_histlen:217446
从信息里可以看到主库的 role 为 master,已连接的从库数量(connected_slaves为 2,然后 slave0 和 slave1 就是从库的信息。
客户端访问 redis-server
客户端如何访问哨兵集群监控的 redis-server 呢?主要有以下几个步骤:
-
连接哨兵集群
。客户端初始化时,需要提供哨兵的地址,客户端会连接到第一个可用的哨兵地址。
连接主库。客户端给哨兵发送 获取主库地址,然后和主库建立连接。为了确认连接的确实是主库,客户端还需要发送 命令来查看 role 是否是 master,如果不是的话就要从第一步重新开始。
连接从库。客户端给哨兵发送 来获取从库信息。相应的,客户端也可以通过 info 命令来确认目标的角色。
监听主库迁移。哨兵通过 pub/sub 来通知客户端集群中的事件。哨兵提供的订阅频道很多,具体可以查看https://redis.io/docs/manual/sentinel/#pubsub-messages 。客户端使用命令 来订阅所有的频道,来了解主从切换的进度。当频道 有新消息时,表示新主库已经切换完成。
实验
version: "3"
services:
redis-master:
image: redis:7
ports:
- "16379:6379"
container_name: "redis-master"
command: redis-server
networks:
- sentinel-network
redis-slave-1:
image: redis:7
ports:
- "6380:6379"
container_name: "redis-slave-1"
command: redis-server --replicaof redis-master 6379
depends_on:
- redis-master
networks:
- sentinel-network
redis-slave-2:
image: redis:7
ports:
- "6381:6379"
container_name: "redis-slave-2"
command: redis-server --replicaof redis-master 6379
depends_on:
- redis-master
networks:
- sentinel-network
redis-sentinel:
image: bitnami/redis-sentinel:latest
environment:
- REDIS_MASTER_HOST=redis-master
- REDIS_SENTINEL_QUORUM=2
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=10000
depends_on:
- redis-master
- redis-slave-1
ports:
- '26379-26381:26379'
networks:
- sentinel-network
networks:
sentinel-network:
使用命令 来启动 docker-compose。启动之后会打出如下的日志:
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.825 * Sentinel new configuration saved on disk
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.825 # Sentinel ID is 787487fa6116b997caa0a44b011793b58e265a54
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.825 # +monitor master mymaster 172.27.0.2 6379 quorum 2
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.827 * +slave slave 172.27.0.3:6379 172.27.0.3 6379 @ mymaster 172.27.0.2 6379
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.840 * Sentinel new configuration saved on disk
redis-sentinel_1 | 1:X 11 Aug 2022 03:59:21.840 * +slave slave 172.27.0.4:6379 172.27.0.4 6379 @ mymaster 172.27.0.2 6379
日志表示这样一个流程:哨兵初始化完成 ⇒ 监控主库 ⇒ 监控从库。
可以使用命令 来模拟主库下线的情况,在执行这个命令之前可以先连到哨兵节点 ,然后用命令 来订阅哨兵的频道,这样模拟主库下线时就能看到哨兵通过频道通知客户端的消息了: